home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / ms_sh21s.zip / SH210 / SRC / SH3.C < prev    next >
C/C++ Source or Header  |  1992-12-14  |  70KB  |  3,230 lines

  1. /* MS-DOS SHELL - Parse Tree Executor
  2.  *
  3.  * MS-DOS SHELL - Copyright (c) 1990,1,2 Data Logic Limited and Charles Forsyth
  4.  *
  5.  * This code is based on (in part) the shell program written by Charles
  6.  * Forsyth and is subject to the following copyright restrictions:
  7.  *
  8.  * 1.  Redistribution and use in source and binary forms are permitted
  9.  *     provided that the above copyright notice is duplicated in the
  10.  *     source form and the copyright notice in file sh6.c is displayed
  11.  *     on entry to the program.
  12.  *
  13.  * 2.  The sources (or parts thereof) or objects generated from the sources
  14.  *     (or parts of sources) cannot be sold under any circumstances.
  15.  *
  16.  *    $Header: /usr/users/istewart/src/shell/sh2.1/RCS/sh3.c,v 2.5 1992/12/14 10:54:56 istewart Exp $
  17.  *
  18.  *    $Log: sh3.c,v $
  19.  *    Revision 2.5  1992/12/14  10:54:56  istewart
  20.  *    BETA 215 Fixes and 2.1 Release
  21.  *
  22.  *    Revision 2.4  1992/11/06  10:03:44  istewart
  23.  *    214 Beta test updates
  24.  *
  25.  *    Revision 2.3  1992/09/03  18:54:45  istewart
  26.  *    Beta 213 Updates
  27.  *
  28.  *    Revision 2.2  1992/07/16  14:33:34  istewart
  29.  *    Beta 212 Baseline
  30.  *
  31.  *    Revision 2.1  1992/07/10  10:52:48  istewart
  32.  *    211 Beta updates
  33.  *
  34.  *    Revision 2.0  1992/05/07  20:31:39  Ian_Stewartson
  35.  *    MS-Shell 2.0 Baseline release
  36.  *
  37.  */
  38.  
  39. #include <sys/types.h>
  40. #include <sys/stat.h>
  41. #include <stdio.h>
  42. #include <signal.h>
  43. #include <errno.h>
  44. #include <setjmp.h>
  45. #include <ctype.h>
  46. #include <string.h>
  47. #include <unistd.h>
  48. #include <stdlib.h>
  49. #include <fcntl.h>
  50. #include <limits.h>
  51. #include <dirent.h>
  52. #include <ctype.h>
  53. #ifdef OS2
  54. #define INCL_DOSSESMGR
  55. #define INCL_DOSQUEUES
  56. #define INCL_DOSPROCESS
  57. #define INCL_DOSERRORS
  58. #include <os2.h>
  59. #else
  60. #include <dos.h>
  61. #endif
  62.  
  63. #include "sh.h"
  64.  
  65. /*
  66.  * Save struct for Parameters ($1, $2 etc)
  67.  */
  68.  
  69. typedef struct SaveParameters {
  70.     char    **Array;        /* The parameters        */
  71.     int        Count;            /* Number of them        */
  72. } SaveParameters;
  73.  
  74. /* static Function and string declarations */
  75.  
  76. static int near        ForkAndExecute (C_Op *, int, int, int, char **,
  77.                     char **);
  78. static bool near    WriteToExtendedFile (int, char *);
  79. static C_Op ** near    SearchCasePatternMatch (C_Op *, char *);
  80. static void near    EchoCurrentCommand (char **);
  81. static int near        ExecuteProgram (char *, char **, char **, int);
  82. static bool near    CheckParameterLength (char **);
  83. static void near    SaveNumericParameters (char **, SaveParameters *);
  84. static void near    RestoreTheParameters (SaveParameters *);
  85. static bool near    ExecuteFunction (char **, int *, bool);
  86. static void near    PrintLoadError (char *);
  87. #ifndef OS2
  88. static bool near    Get_EMS_Driver (void);
  89. static bool near    Get_XMS_Driver (void);
  90. static bool near    EMS_error (char *, int);
  91. static int near        EMS_Close (void);
  92. static bool near    XMS_error (char *, int);
  93. static int near        XMS_Close (void);
  94. static int near        SwapToDiskError (int, char *);
  95. static int near        SwapToMemory (int, char **);
  96. static int near        SpawnProcess (char **);
  97. #endif
  98. static char * near    ConvertErrorNumber (void);
  99. static int near        BuildCommandLine (char *, char **, char **, int);
  100. static int near        StartTheProcess (char *, char **, char **, int);
  101. static int near        SetCommandReturnStatus (int);
  102. static char * near    GenerateFullExecutablePath (char *);
  103. static char ** near    FindNumberOfValues (char **, int *);
  104. static int near        ExecuteScriptFile (char *, char **, char **, int, bool);
  105. static char * near    BuildOS2String (char **, char);
  106. static int near        EnvironExecute (char **, int);
  107. static int near     LocalExecve (char **, char **, int);
  108. static unsigned int near CheckForCommonOptions (LineFields *, int);
  109. static char        **ProcessSpaceInParameters (char **);
  110. static int near        CountDoubleQuotes (char *);
  111. #ifdef OS2
  112. static char        *InsertCharacterAtStart (char *);
  113. static int near        StartTheSession (STARTDATA *, char *, char **, char **);
  114. static int near        OS2_DosExecProgram (int, char *, char **, char **,
  115.                         unsigned int);
  116. #endif
  117.  
  118. static char    *AE2big = "arg/env list too big";
  119. static char    *RootDirectory = "x:\\";
  120.             /* Extended Command line processing file name    */
  121. static char    *Extend_file = (char *)NULL;
  122. static char    *DoubleQuotes = "\"";
  123.  
  124. /* Swapping messages */
  125.  
  126. #ifndef OS2
  127. static char    *NoSwapFiles = "No Swap files\n";
  128. static char    *MS_emsg = "Warning: %s Error (%x)\n";
  129. static char    *MS_Space = "Warning: %s out of space\n";
  130. static char    *SwapFailed = "%s swap failed (%x)\n";
  131. static char    *Swap_File = (char *)NULL;    /* Swap file    */
  132. #endif
  133.  
  134. /*
  135.  * OS2 load error mode
  136.  */
  137. #ifdef OS2
  138. static char    FailName[NAME_MAX + PATH_MAX + 3];
  139. #endif
  140.  
  141. /*
  142.  * Common fields in EXTENDED_LINE file
  143.  */
  144.  
  145. #define COMMON_FIELD_COUNT    5
  146.  
  147. static struct CommonFields {
  148.     char        *Name;
  149.     unsigned int    Flag;
  150. } CommonFields [] = {
  151.     { "switch",        EP_CONVERT },
  152.     { LIT_export,    EP_EXPORT },
  153.     { "noswap",        EP_NOSWAP },
  154.     { "noexpand",    EP_NOEXPAND },
  155. };
  156.  
  157. /*
  158.  * execute tree recursively
  159.  */
  160.  
  161. int ExecuteParseTree (register C_Op *t, int StandardIN, int StandardOUT,
  162.               int Actions)
  163. {
  164.     register C_Op    **tp;
  165.     int            Count;
  166.     int            LocalPipeFP;
  167. #ifdef OS2
  168.     int            ReadPipeFP;
  169.     int            WritePipeFP;
  170. #endif
  171.     char        *cp, **wp;
  172.     char        **Local_Tword;
  173.     Break_C        BreakContinue;
  174.     Break_C        *S_RList;    /* Save link pointers        */
  175.     Break_C        *S_BList;
  176.     Break_C        *S_SList;
  177.     GetoptsIndex    GetoptsSave;
  178.     int            Local_depth;    /* Save local values        */
  179.     int            Local_MemoryAreaLevel;
  180.     int            RetVal = 0;    /* Return value            */
  181.     char        *InputBuffer;    /* Select input Buffer        */
  182.     char        *EndIB;        /* End of buffer        */
  183.     char        *LastWord = null;
  184.  
  185. /* End of tree ? */
  186.  
  187.     if (t == (C_Op *)NULL)
  188.     return 0;
  189.  
  190. /* Save original and Increment execute function recursive level */
  191.  
  192.     Local_depth = Execute_stack_depth++;
  193.  
  194. /* Save original and increment area number */
  195.  
  196.     Local_MemoryAreaLevel = MemoryAreaLevel++;
  197.  
  198. /* Save the exit points from SubShells, functions and for/whiles */
  199.  
  200.     S_RList = Return_List;
  201.     S_BList = Break_List;
  202.     S_SList = SShell_List;
  203.  
  204. /* Expand any arguments */
  205.  
  206.     wp = (char **)NULL;
  207.  
  208.     if ((Local_Tword = t->words) != (char **)NULL)
  209.     {
  210.     if (t->type != TCOM)
  211.         wp = eval (Local_Tword, EXPAND_ALL & ~EXPAND_MOVE,
  212.                (struct ExecutableProcessing *)NULL);
  213.  
  214.     else
  215.     {
  216.         struct ExecutableProcessing     SaveValues;
  217.  
  218.         wp = eval (Local_Tword, EXPAND_ALL, &SaveValues);
  219.         ExecProcessingMode = SaveValues;
  220.     }
  221.      }
  222.  
  223. /* Switch on tree node type */
  224.  
  225.     switch (t->type)
  226.     {
  227.     case TFUNC:            /* name () { list; }    */
  228.         RetVal = SaveFunction (t) ? 0 : 1;
  229.         SetCommandReturnStatus (RetVal);
  230.         break;
  231.  
  232. /* In the case of a () command string, we need to save and restore the
  233.  * current environment, directory and traps (can't think of anything else).
  234.  * For any other, we just restore the current directory.  Also, we don't
  235.  * want changes in the Variable list header saved for SubShells, because
  236.  * we are effectively back at execute depth zero.
  237.  */
  238.     case TPAREN:            /* ()            */
  239.         if ((RetVal = CreateGlobalVariableList (FLAGS_NONE)) == -1)
  240.         break;
  241.  
  242. /* Save Getopts pointers */
  243.  
  244.         GetGetoptsValues (&GetoptsSave);
  245.  
  246.     
  247.         if (setjmp (BreakContinue.CurrentReturnPoint) == 0)
  248.         {
  249.         Return_List = (Break_C *)NULL;
  250.         Break_List  = (Break_C *)NULL;
  251.         BreakContinue.NextExitLevel  = SShell_List;
  252.         SShell_List = &BreakContinue;
  253.         RetVal = ForkAndExecute (t, StandardIN, StandardOUT, Actions,
  254.                      wp, &LastWord);
  255.         }
  256.  
  257. /* Restore the original environment */
  258.  
  259.         else
  260.         RetVal = (int)GetVariableAsNumeric (StatusVariable);
  261.  
  262.         SaveGetoptsValues (GetoptsSave.Index, GetoptsSave.SubIndex);
  263.         Return_List    = S_RList;
  264.         Break_List    = S_BList;
  265.         SShell_List    = S_SList;
  266.         RestoreEnvironment (RetVal, Local_depth);
  267.         break;
  268.  
  269. /* After a normal command, we need to restore the original directory.  Note
  270.  * that a cd will have updated the variable $~, so no problem
  271.  */
  272.  
  273.     case TCOM:            /* A command process    */
  274.         RetVal = ForkAndExecute (t, StandardIN, StandardOUT, Actions, wp,
  275.                      &LastWord);
  276.         RestoreEnvironment (RetVal, Local_depth);
  277.  
  278. /* Save last word if appropriate */
  279.  
  280.         if (!(DisabledVariables & DISABLE_LASTWORD))
  281.         {
  282.         SetVariableFromString (LastWordVariable, LastWord);
  283.         SetVariableStatus (LastWordVariable, STATUS_EXPORT);
  284.         }
  285.         break;
  286.  
  287.     case TPIPE:            /* Pipe processing        */
  288. #ifdef OS2
  289.  
  290. /* Do we want to use real pipes under OS2? */
  291.  
  292.         if (GlobalFlags & FLAGS_REALPIPES)
  293.         {
  294.         if (DosMakePipe ((PHFILE) &ReadPipeFP, (PHFILE) &WritePipeFP, 0))
  295.             break;
  296.  
  297. /* Remap the IO handler */
  298.  
  299.         ReadPipeFP = ReMapIOHandler (ReadPipeFP);
  300.         WritePipeFP = ReMapIOHandler (WritePipeFP);
  301.         DosSetFHandState (ReadPipeFP, OPEN_FLAGS_NOINHERIT);
  302.         DosSetFHandState (WritePipeFP, OPEN_FLAGS_NOINHERIT);
  303.  
  304. /* Is this a foreground thingy? */
  305.  
  306.         if (!(Actions & (EXEC_SPAWN_NOWAIT | EXEC_SPAWN_IGNOREWAIT)))
  307.         {
  308.             int        WaitPid;
  309.             WaitPid = ExecuteParseTree (t->left, StandardIN,
  310.                         WritePipeFP,
  311.                             EXEC_SPAWN_IGNOREWAIT);
  312.             close (WritePipeFP);
  313.             RetVal = ExecuteParseTree (t->right, ReadPipeFP,
  314.                            StandardOUT, Actions);
  315.             close (ReadPipeFP);
  316.             cwait (&WaitPid, WaitPid, WAIT_GRANDCHILD);
  317.         }
  318.  
  319. /* Background processing */
  320.  
  321.         else
  322.         {
  323.             ExecuteParseTree (t->left, StandardIN, WritePipeFP,
  324.                       EXEC_SPAWN_IGNOREWAIT);
  325.             close (WritePipeFP);
  326.             RetVal = ExecuteParseTree (t->right, ReadPipeFP,
  327.                            StandardOUT, Actions);
  328.             close (ReadPipeFP);
  329.         }
  330.  
  331.         break;
  332.         }
  333. #endif
  334.  
  335. /* MSDOS or OS/2 without real pipes - use files.  Safer */
  336.  
  337.         if ((RetVal = OpenAPipe ()) < 0)
  338.         break;
  339.  
  340. /* Create pipe, execute command, reset pipe, execute the other side, close
  341.  * the pipe and fini
  342.  */
  343.  
  344.         LocalPipeFP = ReMapIOHandler (RetVal);
  345.         ExecuteParseTree (t->left, StandardIN, LocalPipeFP, 0);
  346.         lseek (LocalPipeFP, 0L, SEEK_SET);
  347.         RetVal = ExecuteParseTree (t->right, LocalPipeFP, StandardOUT, 0);
  348.         CloseThePipe (LocalPipeFP);
  349.         break;
  350.  
  351.     case TLIST:            /* Entries in a for statement    */
  352.         ExecuteParseTree (t->left, StandardIN, StandardOUT, 0);
  353.         RetVal = ExecuteParseTree (t->right, StandardIN, StandardOUT, 0);
  354.         break;
  355.  
  356.     case TASYNC:            /* Async - not supported    */
  357. #ifdef OS2
  358.         RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT,
  359.                        EXEC_SPAWN_NOWAIT);
  360. #else
  361.         RetVal = -1;
  362.  
  363.         if (!FL_TEST ('w'))
  364.         PrintWarningMessage ("sh: Async commands not supported\n");
  365.  
  366.         SetCommandReturnStatus (RetVal);
  367. #endif
  368.         break;
  369.  
  370.     case TOR:            /* || and &&            */
  371.     case TAND:
  372.         RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT, 0);
  373.  
  374.         if ((t->right != (C_Op *)NULL) &&
  375.         ((RetVal == 0) == (t->type == TAND)))
  376.         RetVal = ExecuteParseTree (t->right, StandardIN, StandardOUT,
  377.                        0);
  378.  
  379.         break;
  380.  
  381.  
  382. /* for x do...done and for x in y do...done - find the start of the variables
  383.  * count the number.
  384.  */
  385.     case TFOR:
  386.     case TSELECT:
  387.         wp = FindNumberOfValues (wp, &Count);
  388.  
  389.  
  390. /* Set up a long jump return point before executing the for function so that
  391.  * the continue statement is executed, ie we reprocessor the for condition.
  392.  */
  393.  
  394.         while (RetVal = setjmp (BreakContinue.CurrentReturnPoint))
  395.         {
  396.  
  397. /* Restore the current stack level and clear out any I/O */
  398.  
  399.         RestoreEnvironment (0, Local_depth + 1);
  400.         Return_List = S_RList;
  401.         SShell_List = S_SList;
  402.  
  403. /* If this is a break - clear the variable and terminate the while loop and
  404.  * switch statement
  405.  */
  406.  
  407.         if (RetVal == BC_BREAK)
  408.             break;
  409.         }
  410.  
  411.         if (RetVal == BC_BREAK)
  412.         break;
  413.  
  414. /* Process the next entry - Add to the break/continue chain */
  415.  
  416.         BreakContinue.NextExitLevel = Break_List;
  417.         Break_List = &BreakContinue;
  418.  
  419. /* Execute the command tree */
  420.  
  421.         if (t->type == TFOR)
  422.         {
  423.         while (Count--)
  424.         {
  425.             SetVariableFromString (t->str, *wp++);
  426.             RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT,
  427.                            0);
  428.         }
  429.         }
  430.  
  431. /* Select option */
  432.  
  433.         else if (!Count)
  434.         /* SKIP */;
  435.  
  436. /* Get some memory for the select input buffer */
  437.  
  438.         else if ((InputBuffer = AllocateMemoryCell (LINE_MAX))
  439.             == (char *)NULL)
  440.         {
  441.         ShellErrorMessage (Outofmemory1);
  442.         RetVal = -1;
  443.         }
  444.  
  445. /* Process the select command */
  446.  
  447.         else
  448.         {
  449.         bool    OutputList = TRUE;
  450.  
  451.         EndIB = &InputBuffer[LINE_MAX - 2];
  452.  
  453.         while (TRUE)
  454.         {
  455.             int        ReadCount;    /* Local counter    */
  456.             int        OnlyDigits;    /* Only digits in string*/
  457.  
  458. /* Output list of words */
  459.  
  460.             if (OutputList)
  461.             {
  462.             for (ReadCount = 0; ReadCount < Count; ReadCount++)
  463.                 fprintf (stderr, "%d: %s\n", ReadCount,
  464.                      wp[ReadCount]);
  465.  
  466.             OutputList = FALSE;
  467.             }
  468.  
  469. /* Output prompt */
  470.  
  471.             OutputUserPrompt (PS3);
  472.             OnlyDigits = 1;
  473.  
  474. /* Read in until end of line, file or a field separator is detected */
  475.  
  476.             for (cp = InputBuffer; (cp < EndIB); cp++)
  477.             {
  478.             if (((ReadCount = read (STDIN_FILENO, cp, 1)) != 1) ||
  479.                 (*cp == CHAR_NEW_LINE))
  480.             {
  481.                 break;
  482.             }
  483.  
  484.             OnlyDigits = OnlyDigits && isdigit (*cp);
  485.             }
  486.  
  487.             *cp = 0;
  488.  
  489. /* Check for end of file */
  490.  
  491.             if (ReadCount != 1)
  492.             break;
  493.  
  494. /* Check for empty line */
  495.  
  496.             if (!strlen (InputBuffer))
  497.             {
  498.             OutputList = TRUE;
  499.             continue;
  500.             }
  501.  
  502.             SetVariableFromString (LIT_REPLY, InputBuffer);
  503.  
  504. /* Check that OnlyDigits is a valid number in the select range */
  505.  
  506.             if (OnlyDigits &&
  507.             ((OnlyDigits = atoi (InputBuffer)) >= 0) &&
  508.             (OnlyDigits < Count))
  509.             SetVariableFromString (t->str, wp[OnlyDigits]);
  510.  
  511.             else
  512.             SetVariableFromString (t->str, null);
  513.  
  514.             RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT,
  515.                            0);
  516.         }
  517.         }
  518.  
  519. /* Remove this tree from the break list */
  520.  
  521.         Break_List = S_BList;
  522.         break;
  523.  
  524. /* While and Until function.  Similar to the For function.  Set up a
  525.  * long jump return point before executing the while function so that
  526.  * the continue statement is executed OK.
  527.  */
  528.  
  529.     case TWHILE:            /* WHILE and UNTIL functions    */
  530.     case TUNTIL:
  531.         while (RetVal = setjmp (BreakContinue.CurrentReturnPoint))
  532.         {
  533.  
  534. /* Restore the current stack level and clear out any I/O */
  535.  
  536.         RestoreEnvironment (0, Local_depth + 1);
  537.         Return_List = S_RList;
  538.         SShell_List = S_SList;
  539.  
  540. /* If this is a break, terminate the while and switch statements */
  541.  
  542.         if (RetVal == BC_BREAK)
  543.             break;
  544.         }
  545.  
  546.         if (RetVal == BC_BREAK)
  547.         break;
  548.  
  549. /* Set up links */
  550.  
  551.         BreakContinue.NextExitLevel = Break_List;
  552.         Break_List = &BreakContinue;
  553.  
  554.         while ((ExecuteParseTree (t->left, StandardIN, StandardOUT, 0) == 0)
  555.                 == (t->type == TWHILE))
  556.         RetVal = ExecuteParseTree (t->right, StandardIN, StandardOUT,
  557.                        0);
  558.  
  559.         Break_List = S_BList;
  560.         break;
  561.  
  562.     case TIF:            /* IF and ELSE IF functions    */
  563.     case TELIF:
  564.         if (t->right != (C_Op *)NULL)
  565.         RetVal = ExecuteParseTree (!ExecuteParseTree (t->left,
  566.                                   StandardIN,
  567.                                   StandardOUT, 0)
  568.                         ? t->right->left : t->right->right,
  569.                         StandardIN, StandardOUT, 0);
  570.  
  571.         break;
  572.  
  573.     case TCASE:            /* CASE function        */
  574.         if ((cp = evalstr (t->str, DOSUB | DOTRIM)) == (char *)NULL)
  575.         cp = null;
  576.  
  577.         if ((tp = SearchCasePatternMatch (t->left, cp)) != (C_Op **)NULL)
  578.         RetVal = ExecuteParseTree (*tp, StandardIN, StandardOUT, 0);
  579.  
  580.         break;
  581.  
  582.     case TBRACE:            /* {} statement            */
  583.         if ((RetVal >= 0) && (t->left != (C_Op *)NULL))
  584.         RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT,
  585.                        (Actions & EXEC_FUNCTION));
  586.  
  587.         break;
  588.     }
  589.  
  590. /* Processing Completed - Restore environment */
  591.  
  592.     t->words        = Local_Tword;
  593.     Execute_stack_depth = Local_depth;
  594.  
  595. /* Remove unwanted malloced space */
  596.  
  597.     FreeAllHereFiles (MemoryAreaLevel);
  598.     ReleaseMemoryArea (MemoryAreaLevel);
  599.  
  600.     MemoryAreaLevel = Local_MemoryAreaLevel;
  601.  
  602. /* Check for traps */
  603.  
  604.     if (t->type == TCOM)
  605.     {
  606.     RunTrapCommand (-1);        /* Debug trap            */
  607.  
  608.     if (RetVal)
  609.         RunTrapCommand (-2);    /* Err trap            */
  610.     }
  611.  
  612. /* Interrupt traps */
  613.  
  614.     if ((Count = InterruptTrapPending) != 0)
  615.     {
  616.     InterruptTrapPending = 0;
  617.     RunTrapCommand (Count);
  618.     }
  619.  
  620. /* Check for interrupts */
  621.  
  622.     if (Interactive () && SW_intr)
  623.     {
  624.     CloseAllHandlers ();
  625.     TerminateCurrentEnvironment ();
  626.     }
  627.  
  628.     return RetVal;
  629. }
  630.  
  631. /*
  632.  * Restore the original directory
  633.  */
  634.  
  635. void RestoreCurrentDirectory (char *path)
  636. {
  637.     SetCurrentDrive (tolower(*path) - 'a' + 1);
  638.  
  639.     if (chdir (&path[2]) != 0)
  640.     {
  641.     if (!FL_TEST ('w'))
  642.         fputs ("Warning: current directory reset to /\n", stderr);
  643.  
  644.     chdir (DirectorySeparator);
  645.     GetCurrentDirectory ();
  646.     }
  647. }
  648.  
  649. /*
  650.  * Ok - execute the program, resetting any I/O required
  651.  */
  652.  
  653. static int near    ForkAndExecute (register C_Op *t, int StandardIN,
  654.                 int StandardOUT, int ForkAction, char **wp,
  655.                 char **LastWord)
  656. {
  657.     int            RetVal = -1;    /* Return value            */
  658.     int            (*shcom)(int, char **) = (int (*)())NULL;
  659.     char        *cp;
  660.     IO_Actions        **iopp;
  661.     char        **owp = wp;
  662.     int            builtin = 0;    /* Builtin function        */
  663.     int            LS_depth = Execute_stack_depth;
  664.     bool        CGVLCalled = FALSE;
  665.     bool        InternalExec = FALSE;
  666.  
  667.     if (t->type == TCOM)
  668.     {
  669.  
  670. /* Malloc failed somewhere - given up.  Also clear the second time
  671.  * exit flag (if OS2) so that the 'jobs active' message is not displayed.
  672.  */
  673.  
  674.     if (wp == (char **)NULL)
  675.     {
  676. #ifdef OS2
  677.         ExitWithJobsActive = FALSE;
  678. #endif
  679.         return SetCommandReturnStatus (-1);
  680.     }
  681.  
  682. /* Skip over any assignments */
  683.  
  684.     while ((cp = *wp++) != (char *)NULL)
  685.         continue;
  686.  
  687.     cp = *wp;
  688.  
  689. /* strip all initial assignments not correct wrt PATH=yyy command  etc */
  690.  
  691.     if (FL_TEST ('x') ||
  692.        ((CurrentFunction != (FunctionList *)NULL) &&
  693.         CurrentFunction->Traced))
  694.         EchoCurrentCommand (cp != (char *)NULL ? wp : owp);
  695.  
  696. /* Is it only an assignement? */
  697.  
  698.     if ((cp == (char *)NULL) && (t->ioact == (IO_Actions **)NULL))
  699.     {
  700.         while (((cp = *(owp++)) != (char *)NULL) &&
  701.            AssignVariableFromString (cp))
  702.  
  703.         continue;
  704. #ifdef OS2
  705.         ExitWithJobsActive = FALSE;
  706. #endif
  707.         return SetCommandReturnStatus (0);
  708.     }
  709.  
  710. /* Check for built in commands */
  711.  
  712.     else if (cp != (char *)NULL)
  713.     {
  714.         shcom = IsCommandBuiltIn (cp, &builtin);
  715.         InternalExec = (strcmp (cp, LIT_exec)) == 0 ? TRUE : FALSE;
  716.  
  717. /*
  718.  * Reset the ExitWithJobsActive flag to enable the 'jobs active' on exit
  719.  * message
  720.  */
  721.  
  722. #ifdef OS2
  723.         if (strcmp (cp, LIT_exit))
  724.         ExitWithJobsActive = FALSE;
  725. #endif
  726.     }
  727.  
  728. #ifdef OS2
  729.     else
  730.         ExitWithJobsActive = FALSE;
  731. #endif
  732.     }
  733.  
  734. #ifdef OS2
  735.     else
  736.     ExitWithJobsActive = FALSE;
  737. #endif
  738.  
  739. /* Unix fork simulation? */
  740.  
  741.     t->words = wp;
  742.  
  743. /* If there is a command to execute or we are exec'ing and this is not a
  744.  * TPAREN, save the current environment
  745.  */
  746.  
  747.     if (t->type != TPAREN)
  748.     {
  749.     if (((owp[0] != (char *)NULL) &&
  750.          ((builtin & BLT_SKIPENVIR) != BLT_SKIPENVIR)) ||
  751.         (ForkAction & EXEC_WITHOUT_FORK))
  752.     {
  753.         if (CreateGlobalVariableList (FLAGS_FUNCTION) == -1)
  754.         return -1;
  755.  
  756.         CGVLCalled = TRUE;
  757.     }
  758.  
  759. /* Set up any variables.  Note there is an assumption that
  760.  * AssignVariableFromString sets the equals sign to 0, hiding the value;
  761.  */
  762.  
  763.     while (((cp = *(owp++)) != (char *)NULL) &&
  764.            AssignVariableFromString (cp))
  765.     {
  766.         if (shcom == (int (*)())NULL)
  767.         SetVariableStatus (cp, STATUS_EXPORT | STATUS_LOCAL);
  768.     }
  769.     }
  770.  
  771. /* We cannot close the pipe, because once the exec/spawn has taken place
  772.  * the processing of the pipe is not yet complete.
  773.  */
  774.  
  775.     if (StandardIN != NOPIPE)
  776.     {
  777.     S_dup2 (StandardIN, STDIN_FILENO);
  778.     /*lseek (STDIN_FILENO, 0L, SEEK_SET);*/
  779.     }
  780.  
  781.     if (StandardOUT != NOPIPE)
  782.     {
  783.     fflush (stdout);
  784.     S_dup2 (StandardOUT, STDOUT_FILENO);
  785.     lseek (STDOUT_FILENO, 0L, SEEK_END);
  786.     }
  787.  
  788. /* Set up any other IO required */
  789.  
  790.     fflush (stdout);
  791.     fflush (stderr);
  792.  
  793.     if ((iopp = t->ioact) != (IO_Actions **)NULL)
  794.     {
  795.     while (*iopp != (IO_Actions *)NULL)
  796.     {
  797.         if (SetUpIOHandlers (*iopp++, StandardIN, StandardOUT))
  798.         return RetVal;
  799.     }
  800.     }
  801.  
  802.  
  803. /* All fids above 10 are autoclosed in the exec file because we have used
  804.  * the O_NOINHERIT flag.  Note I patched open.obj to pass this flag to the
  805.  * open function.
  806.  */
  807.  
  808.     if (t->type == TPAREN)
  809.     return RestoreStandardIO (ExecuteParseTree (t->left, NOPIPE, NOPIPE, 0),
  810.                   TRUE);
  811.  
  812. /* Are we just changing the I/O re-direction for the shell ? */
  813.  
  814.     if (wp[0] == NULL)
  815.     {
  816.     if ((ForkAction & EXEC_WITHOUT_FORK) == 0)
  817.         RestoreStandardIO (0, TRUE);
  818.  
  819.     return 0;
  820.     }
  821.  
  822. /*
  823.  * Find the end of the parameters and set up $_ environment variable
  824.  */
  825.  
  826.     owp = wp;
  827.  
  828.     while (*owp != (char *)NULL)
  829.        owp++;
  830.  
  831. /* Move back to last parameter, and save it */
  832.  
  833.     *LastWord = StringCopy (*(owp - 1));
  834.  
  835. /* No - Check for a function the program.  At this point, we need to put
  836.  * in some processing for return.
  837.  */
  838.  
  839.     if (!(builtin & BLT_CURRENT) && ExecuteFunction (wp, &RetVal, CGVLCalled))
  840.     return RetVal;
  841.  
  842. /* Check for another drive or directory in the restricted shell */
  843.  
  844.     if (anys (":/\\", wp[0]) && CheckForRestrictedShell (wp[0]))
  845.     return RestoreStandardIO (-1, TRUE);
  846.  
  847. /* A little cheat to allow us to use the same code to start OS/2 sessions
  848.  * as to load and execute a program
  849.  */
  850.  
  851. #ifdef OS2
  852.     SessionControlBlock = (STARTDATA *)NULL;
  853. #endif
  854.  
  855. /* Ok - execute the program */
  856.  
  857.     if (!(builtin & BLT_CURRENT))
  858.     {
  859.     RetVal = EnvironExecute (wp, ForkAction);
  860.  
  861.     if (ExecProcessingMode.Flags != EP_ENVIRON)
  862.         RetVal = LocalExecve (wp, BuildCommandEnvironment (), ForkAction);
  863.     }
  864.  
  865. /* If we didn't find it, check for internal command
  866.  *
  867.  * Note that the exec command is a special case
  868.  */
  869.  
  870.     if ((builtin & BLT_CURRENT) || ((RetVal == -1) && (errno == ENOENT)))
  871.     {
  872.     if (shcom != (int (*)())NULL)
  873.     {
  874.         if (InternalExec)
  875.         RetVal = doexec (t);
  876.  
  877.         else
  878.         RetVal = (*shcom)(CountNumberArguments (t->words), t->words);
  879.  
  880.         SetCommandReturnStatus (RetVal);
  881.     }
  882.  
  883.     else
  884.         PrintLoadError (wp[0]);
  885.     }
  886.  
  887.     return RestoreStandardIO (RetVal, TRUE);
  888. }
  889.  
  890. /*
  891.  * Restore Local Environment
  892.  */
  893.  
  894. void RestoreEnvironment (int retval, int stack)
  895. {
  896.     Execute_stack_depth = stack;
  897.     DeleteGlobalVariableList ();
  898.     RestoreCurrentDirectory (CurrentDirectory->value);
  899.     RestoreStandardIO (SetCommandReturnStatus (retval), TRUE);
  900. }
  901.  
  902. /*
  903.  * Set up I/O redirection.  0< 1> are ignored as required within pipelines.
  904.  */
  905.  
  906. bool SetUpIOHandlers (register IO_Actions *iop, int pipein, int pipeout)
  907. {
  908.     register int    u;
  909.     char        *cp, *msg;
  910.  
  911.     if (iop->io_unit == IODEFAULT)    /* take default */
  912.     iop->io_unit = (iop->io_flag & (IOREAD | IOHERE)) ? STDIN_FILENO
  913.                               : STDOUT_FILENO;
  914.  
  915. /* Check for pipes */
  916.  
  917.     if ((pipein != NOPIPE) && (iop->io_unit == STDIN_FILENO))
  918.     return FALSE;
  919.  
  920.     if ((pipeout != NOPIPE) && (iop->io_unit == STDOUT_FILENO))
  921.     return FALSE;
  922.  
  923.     msg = (iop->io_flag & (IOREAD | IOHERE)) ? "open" : "create";
  924.  
  925.     if ((iop->io_flag & IOHERE) == 0)
  926.     {
  927.     if ((cp = evalstr (iop->io_name, DOSUB | DOTRIM)) == (char *)NULL)
  928.         return TRUE;
  929.     }
  930.  
  931.     if (iop->io_flag & IODUP)
  932.     {
  933.     if ((cp[1]) || !isdigit (*cp) && *cp != '-')
  934.     {
  935.         ShellErrorMessage ("illegal >& argument (%s)", cp);
  936.         return TRUE;
  937.     }
  938.  
  939.     if (*cp == '-')
  940.         iop->io_flag = IOCLOSE;
  941.  
  942.     iop->io_flag &= ~(IOREAD | IOWRITE);
  943.     }
  944.  
  945. /*
  946.  * When writing to /dev/???, we have to cheat because MSDOS appears to
  947.  * have a problem with /dev/ files after find_first/find_next.
  948.  */
  949.  
  950.     if (((iop->io_flag & ~(IOXHERE | IOTHERE)) == IOWRITE) &&
  951.     (strnicmp (cp, DeviceNameHeader, LEN_DEVICE_NAME_HEADER) == 0))
  952.     iop->io_flag |= IOCAT;
  953.  
  954. /* Open the file in the appropriate mode */
  955.  
  956.     switch (iop->io_flag & ~(IOXHERE | IOTHERE | IOCLOBBER))
  957.     {
  958.     case IOREAD:                /* <            */
  959.         u = S_open (FALSE, cp, O_RDONLY);
  960.         break;
  961.  
  962.     case IOHERE:                /* <<            */
  963.         u = OpenHereFile (iop->io_name, iop->io_flag & IOXHERE);
  964.         cp = "here file";
  965.         break;
  966.  
  967.     case IOWRITE | IOREAD:            /* <>            */
  968.         if (CheckForRestrictedShell (cp))
  969.         return TRUE;
  970.  
  971.         u = S_open (FALSE, cp, O_RDWR);
  972.         break;
  973.  
  974.     case IOWRITE | IOCAT:            /* >>            */
  975.         if (CheckForRestrictedShell (cp))
  976.         return TRUE;
  977.  
  978.         if ((u = S_open (FALSE, cp, O_WRONLY | O_TEXT)) >= 0)
  979.         {
  980.         lseek (u, 0L, SEEK_END);
  981.         break;
  982.         }
  983.  
  984.     case IOWRITE:                /* >            */
  985.         if (CheckForRestrictedShell (cp))
  986.         return TRUE;
  987.  
  988.         if ((GlobalFlags & FLAGS_NOCLOBER) &&
  989.         (!(iop->io_flag & IOCLOBBER)) &&
  990.         (access (CheckDOSFileName (cp), F_OK) == 0))
  991.         {
  992.         u = -1;
  993.         break;
  994.         }
  995.  
  996.         u = S_open (FALSE, cp, O_CMASK, 0666);
  997.         break;
  998.  
  999.     case IODUP:                /* >&            */
  1000.         if (CheckForRestrictedShell (cp))
  1001.         return TRUE;
  1002.  
  1003.         u = S_dup2 (*cp - '0', iop->io_unit);
  1004.         break;
  1005.  
  1006.     case IOCLOSE:                /* >-            */
  1007.         if ((iop->io_unit >= STDIN_FILENO) &&
  1008.         (iop->io_unit <= STDERR_FILENO))
  1009.         S_dup2 (-1, iop->io_unit);
  1010.  
  1011.         S_close (iop->io_unit, TRUE);
  1012.         return FALSE;
  1013.     }
  1014.  
  1015.     if (u < 0)
  1016.     {
  1017.     PrintWarningMessage (LIT_3Strings, cp, "cannot ", msg);
  1018.     return TRUE;
  1019.     }
  1020.  
  1021.     else if (u != iop->io_unit)
  1022.     {
  1023.     S_dup2 (u, iop->io_unit);
  1024.     S_close (u, TRUE);
  1025.     }
  1026.  
  1027.     return FALSE;
  1028. }
  1029.  
  1030. /*
  1031.  * -x flag - echo command to be executed
  1032.  */
  1033.  
  1034. static void near EchoCurrentCommand (register char **wp)
  1035. {
  1036.     register int    i;
  1037.  
  1038.     if ((CurrentFunction != (FunctionList *)NULL) && CurrentFunction->Traced)
  1039.     fprintf (stderr, "%s:", CurrentFunction->tree->str);
  1040.  
  1041.     fputs (GetVariableAsString (PS4, TRUE), stderr);
  1042.  
  1043.     for (i = 0; wp[i] != (char *)NULL; i++)
  1044.     {
  1045.     if (i)
  1046.         fputc (CHAR_SPACE, stderr);
  1047.  
  1048.     fputs (wp[i], stderr);
  1049.     }
  1050.  
  1051.     fputc (CHAR_NEW_LINE, stderr);
  1052. }
  1053.  
  1054. /* Search the case patterns for the command set whose match pattern matches
  1055.  * the case variable
  1056.  */
  1057.  
  1058. static C_Op ** near SearchCasePatternMatch (C_Op *t, char *w)
  1059. {
  1060.     register C_Op    *t1;
  1061.     C_Op        **tp;
  1062.     register char    **wp, *cp;
  1063.  
  1064.     if (t == (C_Op *)NULL)
  1065.     return (C_Op **)NULL;
  1066.  
  1067.     if (t->type == TLIST)
  1068.     {
  1069.     if ((tp = SearchCasePatternMatch (t->left, w)) != (C_Op **)NULL)
  1070.         return tp;
  1071.  
  1072.     t1 = t->right;    /* TPAT */
  1073.     }
  1074.  
  1075.     else
  1076.     t1 = t;
  1077.  
  1078.     for (wp = t1->words; *wp != (char *)NULL;)
  1079.     {
  1080.     if ((cp = evalstr (*(wp++), DOSUB)) &&
  1081.         GeneralPatternMatch (w, cp, FALSE, (char **)NULL, GM_ALL))
  1082.         return &t1->left;
  1083.     }
  1084.  
  1085.     return (C_Op **)NULL;
  1086. }
  1087.  
  1088. /*
  1089.  * Set up the status on exit from a command
  1090.  */
  1091.  
  1092. static int near    SetCommandReturnStatus (int s)
  1093. {
  1094.     SetVariableFromNumeric (StatusVariable, (long)(ExitStatus = s));
  1095.     return s;
  1096. }
  1097.  
  1098. /*
  1099.  * Execute a command
  1100.  */
  1101.  
  1102. int ExecuteACommand (char **argv, int mode)
  1103. {
  1104.     int        RetVal;
  1105.  
  1106.     CheckProgramMode (*argv, &ExecProcessingMode);
  1107.     RetVal = EnvironExecute (argv, 0);
  1108.  
  1109.     if (ExecProcessingMode.Flags != EP_ENVIRON)
  1110.     RetVal = LocalExecve (argv, BuildCommandEnvironment (), mode);
  1111.  
  1112.     if ((RetVal == -1) && (errno == ENOENT))
  1113.     PrintLoadError (*argv);
  1114.  
  1115.     return RetVal;
  1116. }
  1117.  
  1118. /*
  1119.  * PATH-searching interface to execve.
  1120.  */
  1121.  
  1122. static int near LocalExecve (char **argv, char **envp, int ForkAction)
  1123. {
  1124.     int            res;            /* Result        */
  1125.     char        *em;            /* Exit error message    */
  1126.     int            argc = 0;        /* Original # of argcs    */
  1127.     char        *p_name;        /* Program name        */
  1128.     int            i;
  1129.  
  1130. /* If the environment is null - It is too big - error */
  1131.  
  1132.     if (envp == (char **)NULL)
  1133.     em = AE2big;
  1134.  
  1135.     else if ((p_name = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL)
  1136.     em = strerror (ENOMEM);
  1137.  
  1138.     else
  1139.     {
  1140. /* Start off on the search path for the executable file */
  1141.  
  1142.     switch (i = FindLocationOfExecutable (p_name, argv[0]))
  1143.     {
  1144.         case EXTENSION_EXECUTABLE:
  1145.         if ((res = ExecuteProgram (p_name, argv, envp, ForkAction))
  1146.             != -1)
  1147.             return res;
  1148.  
  1149.         break;
  1150.  
  1151. /* Script file */
  1152.  
  1153.         case EXTENSION_BATCH:
  1154.         case EXTENSION_SHELL_SCRIPT:
  1155.         if ((res = ExecuteScriptFile (p_name, argv, envp, ForkAction,
  1156.                  (bool)(i == EXTENSION_SHELL_SCRIPT))) != -1)
  1157.             return res;
  1158.  
  1159.         break;
  1160.     }
  1161.     }
  1162.  
  1163.     if (errno != ENOENT)
  1164.     PrintLoadError (*argv);
  1165.  
  1166.     if (ForkAction & EXEC_WITHOUT_FORK)
  1167.     exit (-1);
  1168.  
  1169.     return -1;
  1170. }
  1171.  
  1172. /*
  1173.  * Run the command produced by generator `f' applied to stream `arg'.
  1174.  */
  1175.  
  1176. int RunGeneratorCommand (IO_Args *argp, int (*f)(IO_State *), bool f_loop,
  1177.              char *name, char **params)
  1178. {
  1179.     Word_B        *swdlist = WordListBlock;
  1180.     Word_B        *siolist = IOActionBlock;
  1181.     jmp_buf        ev, rt;
  1182.     int            *ofail = FailReturnPoint;
  1183.     int            RetVal = -1;
  1184.     Break_C        *S_RList = Return_List;    /* Save loval links    */
  1185.     Break_C        *S_BList = Break_List;
  1186.     int            LS_depth = Execute_stack_depth;
  1187.     int            sjr;
  1188.     C_Op        *outtree;
  1189.     bool        s_ProcessingEXECCommand = ProcessingEXECCommand;
  1190.     SaveParameters    s_Parameters;
  1191.  
  1192. /* Create a new save area */
  1193.  
  1194.     MemoryAreaLevel++;
  1195.  
  1196. /* Set up $0..$n for the command if appropriate */
  1197.  
  1198.     if (params != (char **)NULL)
  1199.     SaveNumericParameters (params, &s_Parameters);
  1200.  
  1201. /* Execute the command */
  1202.  
  1203.     if (CreateNewEnvironment (setjmp (ErrorReturnPoint = ev)) == FALSE)
  1204.     {
  1205.     Return_List = (Break_C *)NULL;
  1206.     Break_List  = (Break_C *)NULL;
  1207.     WordListBlock = (Word_B *)NULL;
  1208.     IOActionBlock = (Word_B *)NULL;
  1209.  
  1210.     AddToIOStack (argp, f, name);
  1211.     e.iobase = e.iop;
  1212.     e.eof_p = (bool)!f_loop;    /* Set EOF processing        */
  1213.     SW_intr = 0;
  1214.     AllowMultipleLines = 0;
  1215.     InParser = FALSE;
  1216.     ProcessingEXECCommand = (!f_loop) ? TRUE : ProcessingEXECCommand;
  1217.  
  1218. /* Read Input (if f_loop is not set, we are processing a . file command)
  1219.  * either for one line or until end of file.
  1220.  */
  1221.     do
  1222.     {
  1223.         if (((sjr = setjmp (FailReturnPoint = rt)) == 0) &&
  1224.         ((outtree = BuildParseTree ()) != (C_Op *)NULL))
  1225.         RetVal = ExecuteParseTree (outtree, NOPIPE, NOPIPE, 0);
  1226.  
  1227. /* Fail or no loop - zap any files if necessary */
  1228.  
  1229.         else if (sjr || f_loop)
  1230.         {
  1231.         ClearExtendedLineFile ();
  1232.         break;
  1233.         }
  1234.  
  1235.     } while (!f_loop);
  1236.  
  1237.     QuitCurrentEnvironment ();
  1238.     }
  1239.  
  1240. /* Restore the environment */
  1241.  
  1242.     Return_List = S_RList;
  1243.     Break_List = S_BList;
  1244.     ProcessingEXECCommand = s_ProcessingEXECCommand;
  1245.     WordListBlock = swdlist;
  1246.     IOActionBlock = siolist;
  1247.     FailReturnPoint = ofail;
  1248.  
  1249. /* Restore $0..$n */
  1250.  
  1251.     if (params != (char **)NULL)
  1252.     RestoreTheParameters (&s_Parameters);
  1253.  
  1254.     RestoreEnvironment (RetVal, LS_depth);
  1255.  
  1256.     ReleaseMemoryArea (MemoryAreaLevel--);
  1257.     return RetVal;
  1258. }
  1259.  
  1260. /* Exec or spawn the program ? */
  1261.  
  1262. static int near    ExecuteProgram (char *path, char **parms, char **envp,
  1263.                 int ForkAction)
  1264. {
  1265.     int            res;
  1266. #ifndef OS2
  1267.     char        *ep;
  1268.     unsigned int    size = 0;
  1269.     int            serrno;
  1270.     unsigned int    c_cur = (unsigned int)(_psp - 1);
  1271.     struct MCB_list    *mp = (struct MCB_list *)((unsigned long)c_cur << 16L);
  1272. #endif
  1273.  
  1274. /* Check to see if the file exists */
  1275.  
  1276.     strcpy (path_line, path);
  1277.  
  1278. /* Check we have access to the file */
  1279.  
  1280.     if (access (CheckDOSFileName (path_line), F_OK) != 0)
  1281.     return SetCommandReturnStatus (-1);
  1282.  
  1283. /* Process the command line.  If no swapping, we have executed the program */
  1284.  
  1285.     res = BuildCommandLine (path_line, parms, envp, ForkAction);
  1286.  
  1287. #ifdef OS2
  1288.     SetWindowName ();
  1289.     ClearExtendedLineFile ();
  1290.     return SetCommandReturnStatus (res);
  1291. #else
  1292.     if ((ExecProcessingMode.Flags & EP_NOSWAP) || (Swap_Mode == SWAP_OFF) ||
  1293.     res || (ForkAction & EXEC_WITHOUT_FORK))
  1294.     {
  1295.     ClearExtendedLineFile ();
  1296.     return SetCommandReturnStatus (res);
  1297.     }
  1298.  
  1299. /* Find the length of the swap area */
  1300.  
  1301.     while ((mp = (struct MCB_list *)((unsigned long)c_cur << 16L))->MCB_type
  1302.         == MCB_CON)
  1303.     {
  1304.     if (c_cur >= 0x9ffe)
  1305.         break;
  1306.  
  1307.     if ((mp->MCB_pid != _psp) && (mp->MCB_pid != 0) &&
  1308.         (mp->MCB_type != MCB_END))
  1309.     {
  1310.         ClearExtendedLineFile ();
  1311.         PrintErrorMessage ("Fatal: Memory chain corrupt\n");
  1312.         return SetCommandReturnStatus (-1);
  1313.     }
  1314.  
  1315.     c_cur += (mp->MCB_len + 1);
  1316.     size += mp->MCB_len + 1;
  1317.     }
  1318.  
  1319. /*
  1320.  * Convert swap size from paragraphs to 16K blocks.
  1321.  */
  1322.  
  1323.     if (size == 0)
  1324.     size = mp->MCB_len + 1;
  1325.  
  1326.     SW_Blocks = (size / 0x0400) + 1;
  1327.     SW_SBlocks = ((size - etext + _psp - 1) / 0x0400) + 1;
  1328.  
  1329. /* OK Now we've set up the FCB's, command line and opened the swap file.
  1330.  * Get some sys info for the swapper and execute my little assembler
  1331.  * function to swap us out
  1332.  */
  1333.  
  1334. /* Ok - 3 methods of swapping */
  1335.  
  1336. /* If expanded memory - try that */
  1337.  
  1338.     if ((Swap_Mode & SWAP_EXPAND) && Get_EMS_Driver ())
  1339.     {
  1340.     SW_Mode = 3;            /* Set Expanded memory swap    */
  1341.  
  1342.     if ((res = SwapToMemory (SWAP_EXPAND, envp)) != -2)
  1343.         return res;
  1344.     }
  1345.  
  1346.     if ((Swap_Mode & SWAP_EXTEND) && Get_XMS_Driver ())
  1347.     {
  1348.     SW_Mode = (SW_fp == -1) ? 2 : 4;/* Set Extended memory or XMS driver */
  1349.  
  1350.     if ((res = SwapToMemory (SWAP_EXTEND, envp)) != -2)
  1351.         return res;
  1352.     }
  1353.  
  1354. /* Try the disk if available */
  1355.  
  1356.     if (Swap_Mode & SWAP_DISK)
  1357.     {
  1358.     SW_Pwrite = 0;
  1359.  
  1360.     if (Swap_File == (char *)NULL)
  1361.         SW_fp = S_open (FALSE, (ep = GenerateTemporaryFileName ()),
  1362.                 O_SMASK, 0600);
  1363.  
  1364.     else
  1365.     {
  1366.         SW_fp = S_open (FALSE, Swap_File, O_SaMASK);
  1367.         SW_Pwrite = 1;
  1368.     }
  1369.  
  1370.     if (SW_fp < 0)
  1371.         return SwapToDiskError (ENOSPC, NoSwapFiles);
  1372.  
  1373. /* Save the swap file name ? */
  1374.  
  1375.     if ((Swap_File == (char *)NULL) &&
  1376.         ((Swap_File = StringSave (ep)) == null))
  1377.         Swap_File = (char *)NULL;
  1378.  
  1379.     SW_Mode = 1;            /* Set Disk file swap        */
  1380.  
  1381. /* Seek to correct location */
  1382.  
  1383.     if (SW_Pwrite)
  1384.     {
  1385.         long    loc = (long)(etext - _psp + 1) * 16L;
  1386.  
  1387.         if (lseek (SW_fp, loc, SEEK_SET) != loc)
  1388.         return SwapToDiskError (ENOSPC, NoSwapFiles);
  1389.     }
  1390.  
  1391. /* Execute the program */
  1392.  
  1393.     res = SpawnProcess (envp);
  1394.  
  1395. /* Close the extended command line file */
  1396.  
  1397.     ClearExtendedLineFile ();
  1398.  
  1399. /* Check for out of swap space */
  1400.  
  1401.     if (res == -2)
  1402.         return SwapToDiskError (errno, "Swap file write failed\n");
  1403.  
  1404. /* Close the swap file */
  1405.  
  1406.     serrno = errno;
  1407.     S_close (SW_fp, TRUE);
  1408.     errno = serrno;
  1409.  
  1410. /* Return the result */
  1411.  
  1412.     return SetCommandReturnStatus (res);
  1413.     }
  1414.  
  1415. /* No swapping available - give up */
  1416.  
  1417.     ClearExtendedLineFile ();
  1418.     PrintErrorMessage ("All Swapping methods failed\n");
  1419.     errno = ENOSPC;
  1420.     return SetCommandReturnStatus (-1);
  1421. #endif
  1422. }
  1423.  
  1424. #ifndef OS2
  1425. /*
  1426.  * OS2 does not require swapping
  1427.  *
  1428.  * Get the XMS Driver information
  1429.  */
  1430.  
  1431. static bool near Get_XMS_Driver (void)
  1432. {
  1433.     union REGS        or;
  1434.     struct SREGS    sr;
  1435.     unsigned int    SW_EMsize;    /* Number of extend memory blks    */
  1436.  
  1437. /* Get max Extended memory pages, and convert to 16K blocks.  If Extended
  1438.  * memory swapping disabled, set to zero
  1439.  */
  1440.  
  1441.     SW_fp = -1;                /* Set EMS/XMS handler not    */
  1442.                     /* defined            */
  1443.  
  1444. /* Is a XMS memory driver installed */
  1445.  
  1446.     or.x.ax = 0x4300;
  1447.     int86 (0x2f, &or, &or);
  1448.  
  1449.     if (or.h.al != 0x80)
  1450.     {
  1451.     or.x.ax = 0x8800;
  1452.     int86 (0x15, &or, &or);
  1453.     SW_EMsize = or.x.ax / 16;
  1454.  
  1455.     if ((SW_EMsize <= SW_Blocks) ||
  1456.          (((long)(SW_EMstart - 0x100000L) +
  1457.           ((long)(SW_Blocks - SW_EMsize) * 16L * 1024L)) < 0L))
  1458.         return XMS_error (MS_Space, 0);
  1459.  
  1460.     else
  1461.         return TRUE;
  1462.     }
  1463.  
  1464. /* Get the driver interface */
  1465.  
  1466.     or.x.ax = 0x4310;
  1467.     int86x (0x2f, &or, &or, &sr);
  1468.     SW_XMS_Driver = (void (*)())((unsigned long)(sr.es) << 16L | or.x.bx);
  1469.  
  1470. /* Support for version 3 of XMS driver */
  1471.  
  1472.     if ((SW_XMS_Gversion () & 0xff00) < 0x0200)
  1473.     return FL_TEST ('w') ? XMS_error ("Warning: %s Version < 2\n", 0)
  1474.                  : FALSE;
  1475.  
  1476.     else if (SW_XMS_Available () < (SW_Blocks * 16))
  1477.     return FL_TEST ('w') ? XMS_error (MS_Space, 0) : FALSE;
  1478.  
  1479.     else if ((SW_fp = SW_XMS_Allocate (SW_Blocks * 16)) == -1)
  1480.     return XMS_error (MS_emsg, errno);
  1481.  
  1482.     return TRUE;
  1483. }
  1484.  
  1485. /* Get the EMS Driver information */
  1486.  
  1487. static bool near Get_EMS_Driver (void)
  1488. {
  1489.     union REGS        or;
  1490.     struct SREGS    sr;
  1491.     char        *sp;
  1492.  
  1493. /* Set EMS/XMS handler not defined */
  1494.  
  1495.     SW_fp = -1;
  1496.  
  1497.     or.x.ax = 0x3567;
  1498.     intdosx (&or, &or, &sr);
  1499.  
  1500.     sp = (char *)((unsigned long)(sr.es) << 16L | 10L);
  1501.  
  1502. /* If not there - disable */
  1503.  
  1504.     if (memcmp ("EMMXXXX0", sp, 8) != 0)
  1505.     return FL_TEST ('w') ? EMS_error ("Warning: %s not available\n", 0)
  1506.                  : FALSE;
  1507.  
  1508.     or.h.ah = 0x40;            /* Check status            */
  1509.     int86 (0x67, &or, &or);
  1510.  
  1511.     if (or.h.ah != 0)
  1512.     return EMS_error (MS_emsg, or.h.ah);
  1513.  
  1514. /* Check version greater than 3.2 */
  1515.  
  1516.     or.h.ah = 0x46;
  1517.     int86 (0x67, &or, &or);
  1518.  
  1519.     if ((or.h.ah != 0) || (or.h.al < 0x32))
  1520.     return FL_TEST ('w') ? EMS_error ("Warning: %s Version < 3.2\n", 0)
  1521.                  : FALSE;
  1522.  
  1523. /*  get page frame address */
  1524.  
  1525.     or.h.ah = 0x41;
  1526.     int86 (0x67, &or, &or);
  1527.  
  1528.     if (or.h.ah != 0)
  1529.     return EMS_error (MS_emsg, or.h.ah);
  1530.  
  1531.     SW_EMSFrame = or.x.bx;        /* Save the page frame        */
  1532.  
  1533. /* Get the number of pages required */
  1534.  
  1535.     or.h.ah = 0x43;
  1536.     or.x.bx = SW_Blocks;
  1537.     int86 (0x67, &or, &or);
  1538.  
  1539.     if (or.h.ah == 0x088)
  1540.     return EMS_error (MS_Space, 0);
  1541.  
  1542.     if (or.h.ah != 0)
  1543.     return EMS_error (MS_emsg, or.h.ah);
  1544.  
  1545. /* Save the EMS Handler */
  1546.  
  1547.     SW_fp = or.x.dx;
  1548.  
  1549. /* save EMS page map */
  1550.  
  1551.     or.h.ah = 0x47;
  1552.     or.x.dx = SW_fp;
  1553.     int86 (0x67, &or, &or);
  1554.  
  1555.     return (or.h.ah != 0) ? EMS_error (MS_emsg, or.h.ah) : TRUE;
  1556. }
  1557.  
  1558. /* Print EMS error message */
  1559.  
  1560. static bool near EMS_error (char *s, int v)
  1561. {
  1562.     PrintWarningMessage (s, "EMS", v);
  1563.     Swap_Mode &= ~(SWAP_EXPAND);
  1564.     EMS_Close ();
  1565.     return FALSE;
  1566. }
  1567.  
  1568. /* Print XMS error message */
  1569.  
  1570. static bool near XMS_error  (char *s, int v)
  1571. {
  1572.     PrintWarningMessage (s, "XMS", v);
  1573.     Swap_Mode &= ~(SWAP_EXTEND);
  1574.     XMS_Close ();
  1575.     return FALSE;
  1576. }
  1577.  
  1578. /* If the XMS handler is defined - close it */
  1579.  
  1580. static int near XMS_Close (void)
  1581. {
  1582.     int        res = 0;
  1583.  
  1584. /* Release XMS page */
  1585.  
  1586.     if (SW_fp != -1)
  1587.     res = SW_XMS_Free (SW_fp);
  1588.  
  1589.     SW_fp = -1;
  1590.     return res;
  1591. }
  1592.  
  1593. /* If the EMS handler is defined - close it */
  1594.  
  1595. static int near EMS_Close (void)
  1596. {
  1597.     union REGS        or;
  1598.     int            res = 0;
  1599.  
  1600.     if (SW_fp == -1)
  1601.     return 0;
  1602.  
  1603. /* Restore EMS page */
  1604.  
  1605.     or.h.ah = 0x48;
  1606.     or.x.dx = SW_fp;
  1607.     int86 (0x67, &or, &or);
  1608.  
  1609.     if (or.h.ah != 0)
  1610.     res = or.h.al;
  1611.  
  1612.     or.h.ah = 0x45;
  1613.     or.x.dx = SW_fp;
  1614.     int86 (0x67, &or, &or);
  1615.  
  1616.     SW_fp = -1;
  1617.     return (res) ? res : or.h.ah;
  1618. }
  1619. #endif
  1620.  
  1621. /* Set up command line.  If the EXTENDED_LINE variable is set, we create
  1622.  * a temporary file, write the argument list (one entry per line) to the
  1623.  * this file and set the command line to @<filename>.  If NOSWAPPING, we
  1624.  * execute the program because I have to modify the argument line
  1625.  */
  1626.  
  1627. static int near BuildCommandLine (char *path, char **argv, char **envp,
  1628.                   int ForkAction)
  1629. {
  1630.     char        **pl = argv;
  1631.     int            res, fd;
  1632.     bool        found;
  1633.     char        *new_args[3];
  1634. #ifdef OS2
  1635.     char        cmd_line[NAME_MAX + PATH_MAX + 3];
  1636. #endif
  1637.  
  1638. /* Translate process name to MSDOS format */
  1639.  
  1640.     if (GenerateFullExecutablePath (path) == (char *)NULL)
  1641.     return -1;
  1642.  
  1643. /* Extended command line processing */
  1644.  
  1645.     Extend_file = (char *)NULL;        /* Set no file        */
  1646.     found = ((ExecProcessingMode.Flags & EP_UNIXMODE) ||
  1647.          (ExecProcessingMode.Flags & EP_DOSMODE)) ? TRUE : FALSE;
  1648.  
  1649. /* Set up a blank command line */
  1650.  
  1651.     res = 0;
  1652.     cmd_line[0] = 0;
  1653.     cmd_line[1] = CHAR_RETURN;
  1654.  
  1655. /* If there are no parameters, or they fit in the DOS command line
  1656.  * - start the process */
  1657.  
  1658.     if ((*(++pl) == (char *)NULL) || CheckParameterLength (pl))
  1659.     return StartTheProcess (path, argv, envp, ForkAction);
  1660.  
  1661. /* If we can use an alternative approach - indirect files, use it */
  1662.  
  1663.     else if (found)
  1664.     {
  1665.     char    **pl1 = pl;
  1666.  
  1667. /* Check parameters don't contain a re-direction parameter */
  1668.  
  1669.     while (*pl1 != (char *)NULL)
  1670.     {
  1671.         if (**(pl1++) == '@')
  1672.         {
  1673.         found = FALSE;
  1674.         break;
  1675.         }
  1676.     }
  1677.  
  1678. /* If we find it - create a temporary file and write the stuff */
  1679.  
  1680.     if ((found) &&
  1681.         ((fd = S_open (FALSE,
  1682.                Extend_file = GenerateTemporaryFileName (), O_CMASK,
  1683.                0600)) >= 0))
  1684.     {
  1685.         if ((Extend_file = StringSave (Extend_file)) == null)
  1686.         Extend_file = (char *)NULL;
  1687.  
  1688. /* Copy to end of list */
  1689.  
  1690.         while (*pl != (char *)NULL)
  1691.         {
  1692.         if (!WriteToExtendedFile (fd, *(pl++)))
  1693.             return -1;
  1694.         }
  1695.  
  1696. /* Completed write OK */
  1697.  
  1698.         close (fd);
  1699.  
  1700. /* Set up cmd_line[1] to contain the filename */
  1701.  
  1702. #ifdef OS2
  1703.         memset (cmd_line, 0, NAME_MAX + PATH_MAX + 3);
  1704. #else
  1705.         memset (cmd_line, 0, CMD_LINE_MAX);
  1706. #endif
  1707.         cmd_line[1] = CHAR_SPACE;
  1708.         cmd_line[2] = '@';
  1709.         strcpy (&cmd_line[3], Extend_file);
  1710.         cmd_line[0] = (char)(strlen (Extend_file) + 2);
  1711.  
  1712. /* Correctly terminate cmd_line in no swap mode */
  1713.  
  1714. #ifndef OS2
  1715.         if (!(ExecProcessingMode.Flags & EP_NOSWAP) &&
  1716.         (Swap_Mode != SWAP_OFF))
  1717.         cmd_line[cmd_line[0] + 2] = CHAR_RETURN;
  1718. #endif
  1719.  
  1720. /* If the name in the file is in upper case - use \ for separators */
  1721.  
  1722.         if (ExecProcessingMode.Flags & EP_DOSMODE)
  1723.         PATH_TO_DOS (&cmd_line[2]);
  1724.  
  1725. /* OK we are ready to execute */
  1726.  
  1727. #ifndef OS2
  1728.         if ((ExecProcessingMode.Flags & EP_NOSWAP) ||
  1729.         (Swap_Mode == SWAP_OFF) || (ForkAction & EXEC_WITHOUT_FORK))
  1730.         {
  1731. #endif
  1732.         new_args[0] = *argv;
  1733.         new_args[1] = &cmd_line[2];
  1734.         new_args[2] = (char *)NULL;
  1735.  
  1736.         return StartTheProcess (path, new_args, envp, ForkAction);
  1737. #ifndef OS2
  1738.         }
  1739.  
  1740.         else
  1741.         return 0;
  1742. #endif
  1743.     }
  1744.     }
  1745.  
  1746.     return -1;
  1747. }
  1748.  
  1749. /*
  1750.  * Clear Extended command line file
  1751.  */
  1752.  
  1753. void ClearExtendedLineFile (void)
  1754. {
  1755.     if (Extend_file != (char *)NULL)
  1756.     {
  1757.     unlink (Extend_file);
  1758.     ReleaseMemoryCell ((void *)Extend_file);
  1759.     }
  1760.  
  1761.     Extend_file = (char *)NULL;
  1762. }
  1763.  
  1764. #ifndef OS2
  1765. /*
  1766.  * Clear Disk swap file file
  1767.  */
  1768.  
  1769. void ClearSwapFile (void)
  1770. {
  1771.     if (Swap_File != (char *)NULL)
  1772.     {
  1773.     unlink (Swap_File);
  1774.     ReleaseMemoryCell ((void *)Swap_File);
  1775.     }
  1776.  
  1777.     Swap_File = (char *)NULL;
  1778. }
  1779. #endif
  1780.  
  1781. /*
  1782.  * Convert the executable path to the full path name
  1783.  */
  1784.  
  1785. static char * near GenerateFullExecutablePath (char *path)
  1786. {
  1787.     char        cpath[PATH_MAX + 4];
  1788.     char        npath[PATH_MAX + NAME_MAX + 4];
  1789.     char        n1path[PATH_MAX + 4];
  1790.     char        *p;
  1791.     int            drive;
  1792.  
  1793. /* Get path in DOS format */
  1794.  
  1795.     PATH_TO_DOS (path);
  1796.  
  1797. #ifndef OS2
  1798.     strupr (path);
  1799. #else
  1800.     if (!IsHPFSFileSystem (path))
  1801.     strupr (path);
  1802. #endif
  1803.  
  1804. /* Get the current path */
  1805.  
  1806.     getcwd (cpath, PATH_MAX + 3);
  1807.     strcpy (npath, cpath);
  1808.  
  1809. /* In current directory ? */
  1810.  
  1811.     if ((p = strrchr (path, '\\')) == (char *)NULL)
  1812.     {
  1813.      p = path;
  1814.  
  1815. /* Check for a:program case */
  1816.  
  1817.      if (*(p + 1) == ':')
  1818.      {
  1819.         p += 2;
  1820.  
  1821. /* Get the path of the other drive */
  1822.  
  1823.         _getdcwd (tolower (*path) - 'a' + 1, npath, PATH_MAX + 3);
  1824.      }
  1825.     }
  1826.  
  1827. /* In root directory */
  1828.  
  1829.     else if ((p - path) == 0)
  1830.     {
  1831.     ++p;
  1832.     strcpy (npath, RootDirectory);
  1833.     *npath = *path;
  1834.     *npath = *cpath;
  1835.     }
  1836.  
  1837.     else if (((p - path) == 2) && (*(path + 1) == ':'))
  1838.     {
  1839.     ++p;
  1840.     strcpy (npath, RootDirectory);
  1841.     *npath = *path;
  1842.     }
  1843.  
  1844. /* Find the directory */
  1845.  
  1846.     else
  1847.     {
  1848.     *(p++) = 0;
  1849.  
  1850. /* Change to the directory containing the executable */
  1851.  
  1852.     drive = (*(path + 1) == ':') ? tolower (*path) - 'a' + 1 : 0;
  1853.  
  1854. /* Save the current directory on this drive */
  1855.  
  1856.     _getdcwd (drive, n1path, PATH_MAX + 3);
  1857.  
  1858. /* Find the directory we want */
  1859.  
  1860.     if (chdir (path) < 0)
  1861.         return (char *)NULL;
  1862.  
  1863.     _getdcwd (drive, npath, PATH_MAX + 3);    /* Save its full name */
  1864.     chdir (n1path);                /* Restore the original */
  1865.  
  1866. /* Restore our original directory */
  1867.  
  1868.     if (chdir (cpath) < 0)
  1869.         return (char *)NULL;
  1870.     }
  1871.  
  1872.     if (npath[strlen (npath) - 1] != '\\')
  1873.     strcat (npath, "\\");
  1874.  
  1875.     strcat (npath, p);
  1876.     return strcpy (path, npath);
  1877. }
  1878.  
  1879. /*
  1880.  * Find the number of values to use for a for or select statement
  1881.  */
  1882.  
  1883. static char ** near FindNumberOfValues (char **wp, int *Count)
  1884. {
  1885.  
  1886. /* select/for x do...done - use the parameter values.  Need to know how many as
  1887.  * it is not a NULL terminated array
  1888.  */
  1889.  
  1890.     if (wp == (char **)NULL)
  1891.     {
  1892.     if ((*Count = ParameterCount) < 0)
  1893.         *Count = 0;
  1894.  
  1895.     return ParameterArray + 1;
  1896.     }
  1897.  
  1898. /* select/for x in y do...done - find the start of the variables and
  1899.  * use them all
  1900.  */
  1901.  
  1902.     while (*wp++ != (char *)NULL)
  1903.     continue;
  1904.  
  1905. /* Save the start and count them */
  1906.  
  1907.     *Count = CountNumberArguments (wp);
  1908.  
  1909.     return wp;
  1910. }
  1911.  
  1912. /*
  1913.  * Count the number of entries in an array
  1914.  */
  1915.  
  1916. int    CountNumberArguments (char **wp)
  1917. {
  1918.     int        Count = 0;
  1919.  
  1920.     while (*(wp++) != (char *)NULL)
  1921.     Count++;
  1922.  
  1923.     return Count;
  1924. }
  1925.  
  1926. /*
  1927.  * Write a command string to the extended file
  1928.  */
  1929.  
  1930. static bool near WriteToExtendedFile (int fd, char *string)
  1931. {
  1932.     char    *sp = string;
  1933.     char    *cp = string;
  1934.     bool    WriteOk = TRUE;
  1935.     int        Length;
  1936.  
  1937.     if (strlen (string))
  1938.     {
  1939.  
  1940. /* Write the string, converting newlines to backslash newline */
  1941.  
  1942.     while (WriteOk && (cp != (char *)NULL))
  1943.     {
  1944.         if ((cp = strchr (sp, '\n')) != (char *)NULL)
  1945.         *cp = 0;
  1946.  
  1947.         if ((Length = strlen (sp)) && (write (fd, sp, Length) != Length))
  1948.         WriteOk = FALSE;
  1949.  
  1950.         if (WriteOk && (cp != (char *)NULL))
  1951.         WriteOk = (write (fd, "\\\n", 2) == 2) ? TRUE : FALSE;
  1952.  
  1953.         sp = cp + 1;
  1954.     }
  1955.     }
  1956.  
  1957.     if (WriteOk && (write (fd, "\n", 1) == 1))
  1958.     return TRUE;
  1959.  
  1960.     close (fd);
  1961.     ClearExtendedLineFile ();
  1962.     errno = ENOSPC;
  1963.     return FALSE;
  1964. }
  1965.  
  1966. /*
  1967.  * Execute or spawn the process
  1968.  */
  1969.  
  1970. static int near StartTheProcess (char *path, char **argv, char **envp,
  1971.                  int ForkAction)
  1972. {
  1973. #ifdef OS2
  1974.     void    (*sig_int)();        /* Interrupt signal        */
  1975.     int        RetVal;
  1976.     USHORT    usType;
  1977.     STARTDATA    stdata;
  1978. #endif
  1979.  
  1980. /* Is this a start session option */
  1981.  
  1982. #ifdef OS2
  1983.     if (SessionControlBlock != (STARTDATA *)NULL)
  1984.     return StartTheSession (SessionControlBlock, path, argv, envp);
  1985. #endif
  1986.  
  1987.     if (ForkAction & EXEC_WITHOUT_FORK)
  1988. #ifdef OS2
  1989.     return OS2_DosExecProgram (OLD_P_OVERLAY, path, argv, envp,
  1990.                    ExecProcessingMode.Flags);
  1991. #else
  1992.     return execve (path, ProcessSpaceInParameters (argv), envp);
  1993. #endif
  1994.  
  1995. #ifndef OS2
  1996.     return ((ExecProcessingMode.Flags & EP_NOSWAP) || (Swap_Mode == SWAP_OFF))
  1997.         ? spawnve (P_WAIT, path, ProcessSpaceInParameters (argv), envp)
  1998.         : 0;
  1999. #else
  2000.  
  2001.     if (ForkAction & (EXEC_SPAWN_DETACH | EXEC_SPAWN_NOWAIT |
  2002.               EXEC_SPAWN_IGNOREWAIT))
  2003.     {
  2004.     int    Mode = (ForkAction & EXEC_SPAWN_DETACH)
  2005.             ? P_DETACH
  2006.             : ((ForkAction & EXEC_SPAWN_IGNOREWAIT)
  2007.                ? P_NOWAITO
  2008.                 : P_NOWAIT);
  2009.  
  2010.     sig_int = signal (SIGINT, SIG_IGN);
  2011.     RetVal = OS2_DosExecProgram (Mode, path, argv, envp,
  2012.                      ExecProcessingMode.Flags);
  2013.     signal (SIGINT, sig_int);
  2014.  
  2015. /* Remove the reference to the temporary file for background tasks */
  2016.  
  2017.     ReleaseMemoryCell ((void *)Extend_file);
  2018.     Extend_file = (char *)NULL;
  2019.  
  2020.     if ((RetVal != -1) && (Mode != P_NOWAITO))
  2021.     {
  2022.         if (Interactive ())
  2023.             fprintf (stderr, (Mode == P_DETACH)
  2024.              ? "[%d] Process %d detached\n" : "[%d] %d\n",
  2025.              AddNewJob (RetVal, path), RetVal);
  2026.  
  2027.         SetVariableFromNumeric ("!", RetVal);
  2028.         RetVal = 0;
  2029.     }
  2030.     }
  2031.  
  2032. /* In OS/2, we need the type of the program because PM programs have to be
  2033.  * started in a session (or at least that was the only way I could get them
  2034.  * to work).
  2035.  */
  2036.  
  2037.     else if (DosQAppType (path, &usType))
  2038.     {
  2039.     errno = ENOENT;
  2040.     return -1;
  2041.     }
  2042.  
  2043. /* In OS/2, need to set signal to default so child will process it */
  2044.  
  2045.     else
  2046.     {
  2047.     if (Interactive ())
  2048.          sig_int = signal (SIGINT, SIG_DFL);
  2049.  
  2050.     if ((usType & 3) == WINDOWAPI)
  2051.     {
  2052.         stdata.Length = sizeof (STARTDATA);
  2053.         stdata.Related = FALSE;
  2054.         stdata.FgBg = FALSE;
  2055.         stdata.TraceOpt = 0;
  2056.         stdata.PgmTitle = (char *)NULL;
  2057.         stdata.TermQ = 0;
  2058.         stdata.Environment = (char *)1;    /* Build Env */
  2059.         stdata.InheritOpt = 0;
  2060.         stdata.SessionType = 3;
  2061.         stdata.IconFile = (char *)NULL;
  2062.         stdata.PgmHandle = 0L;
  2063.         stdata.PgmControl = 8;
  2064.         stdata.InitXPos = 0;
  2065.         stdata.InitYPos = 0;
  2066.         stdata.InitXSize = 100;
  2067.         stdata.InitYSize = 100;
  2068.  
  2069.         RetVal = StartTheSession (&stdata, path, argv, envp);
  2070.     }
  2071.  
  2072.     else
  2073.         RetVal = OS2_DosExecProgram (P_WAIT, path, argv, envp,
  2074.                      ExecProcessingMode.Flags);
  2075.  
  2076.     if (Interactive ())
  2077.         signal (SIGINT, sig_int);
  2078.     }
  2079.  
  2080.     return RetVal;
  2081. #endif
  2082. }
  2083.  
  2084. #ifdef OS2
  2085. static int near StartTheSession (STARTDATA *SessionData, char *path,
  2086.                  char **argv, char **envp)
  2087. {
  2088.     USHORT    usType;
  2089.     USHORT    idSession;
  2090.     USHORT    pid;
  2091.  
  2092. /* Ensure we always start a PM session in PM */
  2093.  
  2094.     if (DosQAppType (path, &usType))
  2095.     {
  2096.     errno = ENOENT;
  2097.     return -1;
  2098.     }
  2099.  
  2100.     if ((usType & 3) == WINDOWAPI)
  2101.     SessionData->SessionType = 3;
  2102.  
  2103.     SessionData->PgmName = path;
  2104.  
  2105.     if ((SessionData->Environment != (char *)NULL) &&
  2106.     ((SessionData->Environment = BuildOS2String (envp, 0)) == (char *)NULL))
  2107.     return -1;
  2108.  
  2109.     ProcessSpaceInParameters (argv);
  2110.  
  2111.     if ((SessionData->PgmInputs = BuildOS2String (&argv[1], 0)) == (char *)NULL)
  2112.     return -1;
  2113.  
  2114.     if (!(usType = DosStartSession (SessionData, &idSession, &pid)))
  2115.     {
  2116.     fprintf (stderr, "Session %d started\n", (int)idSession);
  2117.         SetVariableFromNumeric ("!", idSession);
  2118.     return 0;
  2119.     }
  2120.  
  2121.     else
  2122.     {
  2123.     errno = ENOENT;
  2124.     return -1;
  2125.     }
  2126. }
  2127. #endif
  2128.  
  2129. /*
  2130.  * Build the OS2 format <value>\0<value>\0 etc \0
  2131.  */
  2132.  
  2133. static char * near BuildOS2String (char **Array, char sep)
  2134. {
  2135.     int        i = 0;
  2136.     int        Length = 0;
  2137.     char    *Output;
  2138.     char    *sp, *cp;
  2139.  
  2140.     while ((sp = Array[i++]) != (char *)NULL)
  2141.     Length += strlen (sp) + 1;
  2142.  
  2143.     Length += 2;
  2144.  
  2145.     if ((Output = AllocateMemoryCell (Length)) == (char *)NULL)
  2146.     return (char *)NULL;
  2147.  
  2148.     i = 0;
  2149.     sp = Output;
  2150.  
  2151. /* Build the string */
  2152.  
  2153.     while ((cp = Array[i++]) != (char *)NULL)
  2154.     {
  2155.     while (*sp = *(cp++))
  2156.         ++sp;
  2157.  
  2158.     if (!sep || (Array[i] != (char *)NULL))
  2159.         *(sp++) = sep;
  2160.     }
  2161.  
  2162.     *sp = 0;
  2163.     return Output;
  2164. }
  2165.  
  2166. /*
  2167.  * Find the location of an executable and return it's full path
  2168.  * name
  2169.  */
  2170.  
  2171. static char    *Extensions [] = { null, EXEExtension, COMExtension,
  2172.                        SHELLExtension, BATExtension, };
  2173.  
  2174.  
  2175. int    FindLocationOfExecutable (char *FullPath, char *name)
  2176. {
  2177.     register char    *sp;            /* Path pointers    */
  2178.     char        *ep;
  2179.     char        *xp;            /* In file name pointers */
  2180.     char        *xp1;
  2181.     int            i, fp;
  2182.  
  2183. /* Scan the path for an executable */
  2184.  
  2185.     sp = (any (CHAR_UNIX_DIRECTORY, name) ||
  2186.       (*(name + 1) == ':')) ? null
  2187.                 : GetVariableAsString (PathLiteral, FALSE);
  2188.  
  2189.     do
  2190.     {
  2191.     sp = BuildNextFullPathName (sp, name, FullPath);
  2192.     ep = &FullPath[strlen (FullPath)];
  2193.  
  2194. /* Get start of file name */
  2195.  
  2196.     if ((xp1 = strrchr (FullPath, CHAR_UNIX_DIRECTORY)) == (char *)NULL)
  2197.         xp1 = FullPath;
  2198.  
  2199.     else
  2200.         ++xp1;
  2201.  
  2202. /* Look up all 5 types */
  2203.  
  2204.     for (i = 0; i < 5; i++)
  2205.     {
  2206.         strcpy (ep, Extensions[i]);
  2207.  
  2208.         if (access (CheckDOSFileName (FullPath), F_OK) == 0)
  2209.         {
  2210.  
  2211. /* If no extension or .sh extension, check for shell script */
  2212.  
  2213.         if (((xp = strchr (xp1, '.')) == (char *)NULL) ||
  2214.             (stricmp (xp, SHELLExtension) == 0))
  2215.         {
  2216.             if ((fp = CheckForScriptFile (FullPath, (char **)NULL,
  2217.                           (int *)NULL)) < 0)
  2218.             continue;
  2219.  
  2220.             S_close (fp, TRUE);
  2221.             return EXTENSION_SHELL_SCRIPT;
  2222.         }
  2223.  
  2224.         else if (!stricmp (xp, EXEExtension) ||
  2225.              !stricmp (xp, COMExtension))
  2226.             return EXTENSION_EXECUTABLE;
  2227.  
  2228.         else if (!stricmp (xp, BATExtension))
  2229.             return EXTENSION_BATCH;
  2230.         }
  2231.     }
  2232.     } while (sp != (char *)NULL);
  2233.  
  2234. /* Not found */
  2235.  
  2236.     errno = ENOENT;
  2237.     return EXTENSION_NOT_FOUND;
  2238. }
  2239.  
  2240. /*
  2241.  * Execute a script file
  2242.  */
  2243.  
  2244. static int near ExecuteScriptFile (char *Fullpath, char **argv, char **envp,
  2245.                    int ForkAction, bool ShellScript)
  2246. {
  2247.     register char    *sp;
  2248.     int            res;            /* Result        */
  2249.     char        *params;        /* Script parameters    */
  2250.     int            nargc = 0;        /* # script args    */
  2251.     Word_B        *wb = (Word_B *)NULL;
  2252.     int            j;
  2253.     char        **nargv;
  2254. #ifndef OS2
  2255.     union REGS        r;
  2256. #endif
  2257.  
  2258. /* Batfile - convert to DOS Format file name */
  2259.  
  2260.     if (!ShellScript)
  2261.     {
  2262.     PATH_TO_DOS (Fullpath);
  2263.     nargc = 0;
  2264.     }
  2265.  
  2266.     else if ((res = OpenForExecution (Fullpath, ¶ms, &nargc)) >= 0)
  2267.     S_close (res, TRUE);
  2268.  
  2269.     else
  2270.     {
  2271.     errno = ENOENT;
  2272.     return -1;
  2273.     }
  2274.  
  2275. /* If BAT file, use command.com else use sh */
  2276.  
  2277.     if (!ShellScript)
  2278.     {
  2279.     if ((sp = StringCopy (GetVariableAsString (ComspecVariable,
  2280.                            FALSE))) == null)
  2281.         return -1;
  2282.  
  2283.     wb = SplitString (sp, wb);
  2284.     wb = AddWordToBlock ("/c", wb);
  2285.  
  2286. #ifndef OS2
  2287. /* Get the switch character */
  2288.  
  2289.     r.x.ax = 0x3700;
  2290.     intdos (&r, &r);
  2291.  
  2292.     if ((r.h.al == 0) && (_osmajor < 4))
  2293.         *wb->w_words[1] = (char)(r.h.dl);
  2294. #endif
  2295.     }
  2296.  
  2297. /* Stick in the pre-fix arguments */
  2298.  
  2299.     else if (nargc)
  2300.     wb = SplitString (params, wb);
  2301.  
  2302.     else if (params != null)
  2303.     wb = AddWordToBlock (params, wb);
  2304.  
  2305.     else
  2306.     wb = AddWordToBlock (GetVariableAsString (ShellVariableName, FALSE),
  2307.                  wb);
  2308.  
  2309. /* Add the rest of the parameters */
  2310.  
  2311.     wb = AddWordToBlock (Fullpath, wb);
  2312.  
  2313.     j = 1;
  2314.     while (argv[j] != (char *)NULL)
  2315.     wb = AddWordToBlock (argv[j++], wb);
  2316.  
  2317. /* Execute the program */
  2318.  
  2319.     nargv = GetWordList (AddWordToBlock (NOWORD, wb));
  2320.  
  2321. /* Save the old empty space value and use the max */
  2322.  
  2323.     CheckProgramMode (*nargv, &ExecProcessingMode);
  2324.  
  2325. /* Special to try and sort out command.com.  The only flag we don't carry over
  2326.  * from the original command is the CONVERT flag.  Not that it makes much
  2327.  * difference, since it has already been processed.
  2328.  */
  2329.  
  2330.     if (!ShellScript)
  2331.     ExecProcessingMode.Flags &= ~EP_CONVERT;
  2332.  
  2333.     res = EnvironExecute (nargv, ForkAction);
  2334.  
  2335.     if (ExecProcessingMode.Flags != EP_ENVIRON)
  2336.     res = LocalExecve (nargv, envp, ForkAction);
  2337.  
  2338. /* Release allocated space */
  2339.  
  2340.     if (params != null)
  2341.     ReleaseMemoryCell ((void *)params);
  2342.  
  2343. /* 0 is a special case - see ConvertErrorNumber */
  2344.  
  2345.     if (res == -1)
  2346.     errno = 0;
  2347.  
  2348.     return res;
  2349. }
  2350.  
  2351. /*
  2352.  * Convert errno to error message on execute
  2353.  */
  2354.  
  2355. static char * near ConvertErrorNumber (void)
  2356. {
  2357.     switch (errno)
  2358.     {
  2359.     case ENOMEM:
  2360.         return strerror (ENOMEM);
  2361.  
  2362.     case ENOEXEC:
  2363.         return "program corrupt";
  2364.  
  2365.     case E2BIG:
  2366.         return AE2big;
  2367.  
  2368.     case ENOENT:
  2369.         return NotFound;
  2370.  
  2371.     case 0:
  2372.         return "No Shell";
  2373.     }
  2374.  
  2375.     return "cannot execute";
  2376. }
  2377.  
  2378. #ifndef OS2
  2379. /*
  2380.  * Swap to disk error
  2381.  */
  2382.  
  2383. static int near SwapToDiskError (int error, char *ErrorMessage)
  2384. {
  2385.  
  2386. /* Close the swap file, if open */
  2387.  
  2388.     if (SW_fp >= 0)
  2389.     S_close (SW_fp, TRUE);
  2390.  
  2391. /* Clean up */
  2392.  
  2393.     ClearSwapFile ();
  2394.     Swap_Mode &= (~SWAP_DISK);
  2395.     PrintErrorMessage (ErrorMessage);
  2396.     errno = error;
  2397.     return SetCommandReturnStatus (-1);
  2398. }
  2399.  
  2400. /*
  2401.  * Swap to memory
  2402.  */
  2403.  
  2404. static int near    SwapToMemory (int mode, char **envp)
  2405. {
  2406.     int        res;
  2407.     int        cr;
  2408.  
  2409. /* Swap and close memory handler */
  2410.  
  2411.     res = SpawnProcess (envp);
  2412.  
  2413.     cr = (SW_Mode != 3) ? XMS_Close () : EMS_Close ();
  2414.  
  2415.     if ((res != -2) && cr)        /* Report Close error ?        */
  2416.     {
  2417.     res = -2;
  2418.     errno = cr;
  2419.     }
  2420.  
  2421.     if (res == -2)
  2422.     (SW_Mode != 3) ? XMS_error (SwapFailed, errno)
  2423.                : EMS_error (SwapFailed, errno);
  2424.  
  2425.     else
  2426.     {
  2427.     ClearExtendedLineFile ();
  2428.     return SetCommandReturnStatus (res);
  2429.     }
  2430.  
  2431. /* Failed - disabled */
  2432.  
  2433.     Swap_Mode &= (~mode);
  2434.     return res;
  2435. }
  2436. #endif
  2437.  
  2438. /*
  2439.  * Check the program type
  2440.  */
  2441.  
  2442. void CheckProgramMode (char *Pname, struct ExecutableProcessing *PMode)
  2443. {
  2444.     char        *sp, *sp1;        /* Line pointers    */
  2445.     int            nFields;
  2446.     char        *SPname;
  2447.     int            builtin;        /* Builtin function    */
  2448.     LineFields        LF;
  2449.     long        value;
  2450.  
  2451. /* Check for internal no-globbed commands */
  2452.  
  2453.     if ((IsCommandBuiltIn (Pname, &builtin) != (int (*)())NULL) &&
  2454.     ((builtin & BLT_SKIPGLOB) == BLT_SKIPGLOB))
  2455.     {
  2456.     PMode->Flags = EP_NOEXPAND | ((builtin & BLT_NOWORDS) ? EP_NOWORDS : 0);
  2457.     return;
  2458.     }
  2459.  
  2460. /* Set not found */
  2461.  
  2462.     PMode->Flags = EP_NONE;
  2463.  
  2464. /* Check not a function */
  2465.  
  2466.     if ((Pname == (char *)NULL) ||
  2467.     ((sp = GetVariableAsString ("EXTENDED_LINE", FALSE)) == null))
  2468.         return;
  2469.  
  2470. /* Get some memory for the input line and the file name */
  2471.  
  2472.     sp1 = ((sp1 = strrchr (Pname, CHAR_UNIX_DIRECTORY)) == (char *)NULL)
  2473.          ? Pname : sp1 + 1;
  2474.  
  2475.     if (*(sp1 + 1) == ':')
  2476.     sp1 += 2;
  2477.  
  2478.     if ((SPname = StringCopy (sp1)) == null)
  2479.         return;
  2480.  
  2481.     if ((LF.Line = AllocateMemoryCell (LF.LineLength = 200)) == (char *)NULL)
  2482.     {
  2483.     ReleaseMemoryCell ((void *)SPname);
  2484.     return;
  2485.     }
  2486.  
  2487. /* Remove terminating .exe etc */
  2488.  
  2489.     if ((sp1 = strrchr (SPname, '.')) != (char *)NULL)
  2490.         *sp1 = 0;
  2491.  
  2492. /* Open the file */
  2493.  
  2494.     if ((LF.FP = fopen (sp, "rt")) == (FILE *)NULL)
  2495.     {
  2496.     ReleaseMemoryCell ((void *)LF.Line);
  2497.     ReleaseMemoryCell ((void *)SPname);
  2498.     return;
  2499.     }
  2500.  
  2501. /* Initialise the internal buffer */
  2502.  
  2503.     LF.Fields = (Word_B *)NULL;
  2504.  
  2505. /* Scan for the file name */
  2506.  
  2507.     while ((nFields = ExtractFieldsFromLine (&LF)) != -1)
  2508.     {
  2509.         if (nFields < 2)
  2510.             continue;
  2511.  
  2512. /* Remove terminating .exe etc */
  2513.  
  2514.     if ((sp = strrchr (LF.Fields->w_words[0], '.')) != (char *)NULL)
  2515.         *sp = 0;
  2516.  
  2517.         if (stricmp (LF.Fields->w_words[0], SPname))
  2518.             continue;
  2519.  
  2520. /* What type? */
  2521.  
  2522.     if (stricmp (LF.Fields->w_words[1], "unix") == 0)
  2523.         PMode->Flags = (unsigned int )(EP_UNIXMODE |
  2524.                 CheckForCommonOptions (&LF, 2));
  2525.  
  2526.     else if (stricmp (LF.Fields->w_words[1], "dos") == 0)
  2527.         PMode->Flags = (unsigned int )(EP_DOSMODE |
  2528.                 CheckForCommonOptions (&LF, 2));
  2529.  
  2530. /* Must have a valid name and we can get memory for it */
  2531.  
  2532.     else if ((stricmp (LF.Fields->w_words[1], "environ") == 0) &&
  2533.          (nFields >= 3) &&
  2534.          (!IsValidVariableName (LF.Fields->w_words[2])) &&
  2535.          ((PMode->Name =
  2536.              StringCopy (LF.Fields->w_words[2])) != null))
  2537.     {
  2538.         PMode->Flags = EP_ENVIRON;
  2539.         PMode->FieldSep = 0;
  2540.  
  2541.         if ((nFields >= 4) &&
  2542.         ConvertNumericValue (LF.Fields->w_words[3], &value, 0))
  2543.         PMode->FieldSep = (unsigned char)value;
  2544.  
  2545.         if (!PMode->FieldSep)
  2546.         PMode->FieldSep = ' ';
  2547.     }
  2548.  
  2549.     else
  2550.         PMode->Flags = CheckForCommonOptions (&LF, 1);
  2551.  
  2552.         break;
  2553.     }
  2554.  
  2555.     fclose (LF.FP);
  2556.     ReleaseMemoryCell ((void *)LF.Line);
  2557.     ReleaseMemoryCell ((void *)SPname);
  2558. }
  2559.  
  2560. /*
  2561.  * Check for common fields
  2562.  */
  2563.  
  2564. static unsigned int near CheckForCommonOptions (LineFields *LF, int Start)
  2565. {
  2566.     unsigned int    Flags = 0;
  2567.     int            i, j;
  2568.  
  2569.     if (LF->Fields == (Word_B *)NULL)
  2570.     return 0;
  2571.  
  2572.     for (i = Start; i < LF->Fields->w_nword; i++)
  2573.     {
  2574.     for (j = 0; j < COMMON_FIELD_COUNT; ++j)
  2575.     {
  2576.         if (!stricmp (LF->Fields->w_words[i], CommonFields[j].Name))
  2577.         {
  2578.         Flags |= CommonFields[j].Flag;
  2579.         break;
  2580.         }
  2581.     }
  2582.     }
  2583.  
  2584.     return Flags;
  2585. }
  2586.  
  2587. /*
  2588.  * Convert UNIX format lines to DOS format if appropriate.
  2589.  * Build Environment variable for some programs.
  2590.  */
  2591.  
  2592. static int near EnvironExecute (char **argv, int ForkAction)
  2593. {
  2594.     int        s_errno;
  2595.     int        RetVal = 1;
  2596.     char    *NewArgs[3];
  2597.     char    *cp;
  2598.  
  2599. /* If this command does not pass the command string in the environment,
  2600.  * no action required
  2601.  */
  2602.  
  2603.     if (ExecProcessingMode.Flags != EP_ENVIRON)
  2604.         return 0;
  2605.  
  2606.     if ((cp = BuildOS2String (&argv[1], ExecProcessingMode.FieldSep))
  2607.          == (char *)NULL)
  2608.     {
  2609.     ExecProcessingMode.Flags = EP_NONE;
  2610.         return 0;
  2611.     }
  2612.  
  2613.     SetVariableFromString (ExecProcessingMode.Name, cp);
  2614.     SetVariableStatus (ExecProcessingMode.Name, STATUS_EXPORT);
  2615.  
  2616. /* Build and execute the environment */
  2617.  
  2618.     NewArgs[0] = argv[0];
  2619.     NewArgs[1] = ExecProcessingMode.Name;
  2620.     NewArgs[2] = (char *)NULL;
  2621.  
  2622.     RetVal = LocalExecve (NewArgs, BuildCommandEnvironment (), ForkAction);
  2623.     s_errno = errno;
  2624.     UnSetVariable (ExecProcessingMode.Name, FALSE);
  2625.     errno = s_errno;
  2626.     return RetVal;
  2627. }
  2628.  
  2629. /*
  2630.  * Set Interrupt handling vectors - moved from sh0.asm
  2631.  */
  2632.  
  2633. #ifndef OS2
  2634. static int near    SpawnProcess (char **envp)
  2635. {
  2636.     void    (interrupt far *SW_I00_V) (void);    /* Int 00 address */
  2637.     void    (interrupt far *SW_I23_V) (void);    /* Int 23 address*/
  2638.     int            res;
  2639. #if 0
  2640.     union REGS        r;
  2641.     unsigned char    Save;
  2642.  
  2643.     r.x.ax = 0x3300;
  2644.     intdos (&r, &r);
  2645.     Save = r.h.al;
  2646.     fprintf (stderr, "Break Status: %s (%u)\n", Save ? "on" : "off", Save);
  2647.  
  2648.     r.x.ax = 0x3301;
  2649.     r.h.dl = 1;
  2650.     intdos (&r, &r);
  2651.     fprintf (stderr, "Break Status: %s (%u)\n", r.h.al ? "on" : "off", r.h.al);
  2652. #endif
  2653.  
  2654. /*
  2655.  * Save current vectors
  2656.  */
  2657.  
  2658.     SW_I00_V = _dos_getvect (0x00);
  2659.     SW_I23_V = _dos_getvect (0x23);
  2660.  
  2661. /*
  2662.  * Set In shell flag for Interrupt 23, and set to new interrupts
  2663.  */
  2664.  
  2665.     SW_I23_InShell = 0;
  2666.     _dos_setvect (0x23, SW_Int23);
  2667.     _dos_setvect (0x00, SW_Int00);
  2668.  
  2669.     res = SA_spawn (envp);
  2670.  
  2671. /*
  2672.  * Restore interrupt vectors
  2673.  */
  2674.  
  2675.     _dos_setvect (0x00, SW_I00_V);
  2676.     _dos_setvect (0x23, SW_I23_V);
  2677.  
  2678. #if 0
  2679.     r.x.ax = 0x3300;
  2680.     intdos (&r, &r);
  2681.     fprintf (stderr, "Break Status: %s (%u)\n", r.h.al ? "on" : "off", r.h.al);
  2682.     r.x.ax = 0x3301;
  2683.     r.h.dl = Save;
  2684.     intdos (&r, &r);
  2685. #endif
  2686.  
  2687. /*
  2688.  * Check for an interrupt
  2689.  */
  2690.  
  2691.     if (SW_intr)
  2692.     raise (SIGINT);
  2693.  
  2694.     return res;
  2695. }
  2696. #endif
  2697.  
  2698. /*
  2699.  * Check Parameter line length
  2700.  *
  2701.  * Under OS2, we don't build the command line.  Just check it.
  2702.  */
  2703.  
  2704. static bool near CheckParameterLength (char **argv)
  2705. {
  2706.     int        CmdLineLength;
  2707.     char    *CommandLine;
  2708.     bool    RetVal;
  2709.     char    **SavedArgs = argv;
  2710.  
  2711. /* Check for special case.  If there are any special characters and we can
  2712.  * use UNIX mode, use it
  2713.  */
  2714.  
  2715.     if (ExecProcessingMode.Flags & EP_UNIXMODE)
  2716.     {
  2717.     while (*argv != (char *)NULL)
  2718.     {
  2719.         if (anys ("\"'", *(argv++)))
  2720.         return FALSE;
  2721.     }
  2722.     }
  2723.  
  2724. /*
  2725.  * Do any parameter conversion - adding quotes or backslashes, but don't
  2726.  * update argv.
  2727.  */
  2728.  
  2729.     CmdLineLength = CountNumberArguments (argv = SavedArgs);
  2730.  
  2731.     if ((SavedArgs = (char **)
  2732.         AllocateMemoryCell ((CmdLineLength + 1) * sizeof (char *)))
  2733.         == (char *)NULL)
  2734.     return FALSE;
  2735.  
  2736. /* Save a copy of the argument addresses */
  2737.  
  2738.     memcpy (SavedArgs, argv, (CmdLineLength + 1) * sizeof (char *));
  2739.  
  2740. /* Build the command line */
  2741.  
  2742.     if ((CommandLine = BuildOS2String (ProcessSpaceInParameters (SavedArgs),
  2743.                         CHAR_SPACE)) == (char *)NULL)
  2744.     {
  2745.     ReleaseMemoryCell (SavedArgs);
  2746.     return FALSE;
  2747.     }
  2748.  
  2749. /* Check command line length */
  2750.  
  2751.     if ((CmdLineLength = strlen (CommandLine)) >= CMD_LINE_MAX - 2)
  2752.     {
  2753.     errno = E2BIG;
  2754.     RetVal = FALSE;
  2755.     }
  2756.  
  2757. /* Terminate the line */
  2758.  
  2759.     else
  2760.     {
  2761. #ifndef OS2
  2762.     strcpy (cmd_line + 1, CommandLine);
  2763.     cmd_line[CmdLineLength + 1] = CHAR_RETURN;
  2764.     cmd_line[0] = (char)CmdLineLength;
  2765. #endif
  2766.     RetVal = TRUE;
  2767.     }
  2768.  
  2769.     ReleaseMemoryCell (SavedArgs);
  2770.     ReleaseMemoryCell (CommandLine);
  2771.  
  2772.     return RetVal;
  2773. }
  2774.  
  2775. /*
  2776.  * Convert any parameters with spaces in the to start and end with double
  2777.  * quotes.
  2778.  *
  2779.  * Under OS2, the old string is NOT released.
  2780.  */
  2781.  
  2782. static char **ProcessSpaceInParameters (char **argv)
  2783. {
  2784.     char    **Start = argv;
  2785.     char    *new;
  2786.     char    *cp;
  2787.     char    *sp;
  2788.     int        Count;
  2789.  
  2790. /* Protect parameters with TABS */
  2791.  
  2792.     while (*argv != (char *)NULL)
  2793.     {
  2794.         if ((strchr (*argv, CHAR_SPACE) != (char *)NULL) ||
  2795.         (strchr (*argv, CHAR_TAB) != (char *)NULL)   ||
  2796.         (strlen (*argv) == 0))
  2797.     {
  2798.  
  2799. /* Count number of Double quotes in the parameter */
  2800.  
  2801.         Count = CountDoubleQuotes (*argv);
  2802.  
  2803. /* Get some memory - give up update if out of memory */
  2804.  
  2805.         if ((new = GetAllocatedSpace (strlen (*argv) + (Count * 2) +
  2806.                       3)) == (char *)NULL)
  2807.             return Start;
  2808.  
  2809.         SetMemoryAreaNumber ((void *)new,
  2810.                      GetMemoryAreaNumber ((void *)*argv));
  2811.         *new = CHAR_DOUBLE_QUOTE;
  2812.  
  2813. /* Escape any double quotes in the string */
  2814.  
  2815.         cp = *argv;
  2816.         sp = new + 1;
  2817.  
  2818.         while (*cp)
  2819.         {
  2820.         if (*cp == CHAR_DOUBLE_QUOTE)
  2821.         {
  2822.             *(sp++) = '\\';
  2823.             *(sp++) = *(cp++);
  2824.         }
  2825.  
  2826.         else if (*cp != '\\')
  2827.             *(sp++) = *(cp++);
  2828.  
  2829. /* Handle escapes - count them */
  2830.  
  2831.         else
  2832.         {
  2833.             *(sp++) = *(cp++);
  2834.  
  2835.             if (*cp == CHAR_DOUBLE_QUOTE)
  2836.             {
  2837.             *(sp++) = '\\';
  2838.             *(sp++) = '\\';
  2839.             }
  2840.  
  2841.             else if (*cp == 0)
  2842.             {
  2843.             *(sp++) = '\\';
  2844.             break;
  2845.             }
  2846.  
  2847.             *(sp++) = *(cp++);
  2848.         }
  2849.         }
  2850.  
  2851. /* Append the terminating double quotes */
  2852.  
  2853.         strcpy (sp, DoubleQuotes);
  2854.         *argv = new;
  2855.     }
  2856.  
  2857. /* Check for any double quotes */
  2858.  
  2859.     else if (Count = CountDoubleQuotes (*argv))
  2860.     {
  2861.  
  2862. /* Got them - escape them */
  2863.  
  2864.         if ((new = GetAllocatedSpace (strlen (*argv) + Count + 1))
  2865.             == (char *)NULL)
  2866.             return Start;
  2867.  
  2868.         SetMemoryAreaNumber ((void *)new,
  2869.                      GetMemoryAreaNumber ((void *)*argv));
  2870.  
  2871. /* Copy the string, escaping DoubleQuotes */
  2872.  
  2873.         cp = *argv;
  2874.         sp = new;
  2875.  
  2876.         while (*sp = *(cp++))
  2877.         {
  2878.         if (*sp == CHAR_DOUBLE_QUOTE)
  2879.         {
  2880.             *(sp++) = '\\';
  2881.             *sp = CHAR_DOUBLE_QUOTE;
  2882.         }
  2883.  
  2884.         sp++;
  2885.         }
  2886.  
  2887.         *argv = new;
  2888.     }
  2889.  
  2890. /* Next parameter */
  2891.  
  2892.     argv++;
  2893.     }
  2894.  
  2895.     return Start;
  2896. }
  2897.  
  2898. /*
  2899.  * Count DoubleQuotes
  2900.  */
  2901.  
  2902. static int near CountDoubleQuotes (char *string)
  2903. {
  2904.     int        Count = 0;
  2905.  
  2906.     while ((string = strchr (string, CHAR_DOUBLE_QUOTE)) != (char *)NULL)
  2907.     {
  2908.     Count++;
  2909.     string++;
  2910.     }
  2911.  
  2912.     return Count;
  2913. }
  2914.  
  2915. /*
  2916.  * Save and Restore the Parameters array ($1, $2 etc)
  2917.  */
  2918.  
  2919. static void near SaveNumericParameters (char **wp, SaveParameters *SaveArea)
  2920. {
  2921.     SaveArea->Array = ParameterArray;
  2922.     SaveArea->Count = ParameterCount;
  2923.  
  2924.     ParameterArray = wp;
  2925.     for (ParameterCount = 0;
  2926.      ParameterArray[ParameterCount] != (char *)NULL; ++ParameterCount)
  2927.     continue;
  2928.  
  2929.     SetVariableFromNumeric (ParameterCountVariable, (long)--ParameterCount);
  2930. }
  2931.  
  2932. static void near RestoreTheParameters (SaveParameters *SaveArea)
  2933. {
  2934.     ParameterArray = SaveArea->Array;
  2935.     ParameterCount = SaveArea->Count;
  2936.     SetVariableFromNumeric (ParameterCountVariable, (long)ParameterCount);
  2937. }
  2938.  
  2939. #ifdef OS2
  2940. /*
  2941.  * Special OS/2 processing for execve and spawnve
  2942.  */
  2943.  
  2944. static int near OS2_DosExecProgram (int Mode, char *Program, char **argv,
  2945.                     char **envp, unsigned int Type)
  2946. {
  2947.     USHORT        fExecFlags;
  2948.     RESULTCODES        rescResults;
  2949.     char        *OS2Environment;
  2950.     char        *OS2Arguments;
  2951.     USHORT        ErrorCode;
  2952.     char        **SavedArgs;
  2953.     int            argc;
  2954.  
  2955. /* Set the error module to null */
  2956.  
  2957.     *FailName = 0;
  2958.  
  2959. /* Convert spawn mode to DosExecPgm mode */
  2960.  
  2961.     switch (Mode)
  2962.     {
  2963.     case P_WAIT:
  2964.         fExecFlags = EXEC_SYNC;
  2965.         break;
  2966.  
  2967.     case P_NOWAIT:
  2968.         fExecFlags = EXEC_ASYNCRESULT;
  2969.         break;
  2970.  
  2971.     case P_NOWAITO:
  2972.     case OLD_P_OVERLAY:
  2973.         fExecFlags = EXEC_ASYNC;
  2974.         break;
  2975.  
  2976.     case P_DETACH:
  2977.         fExecFlags = EXEC_BACKGROUND;
  2978.         break;
  2979.     }
  2980.  
  2981. /* Build OS/2 argument string
  2982.  *
  2983.  * 1.  Count the number of arguments.
  2984.  * 2.  Add 2 for: 1 - NULL; 2 - argv[0]; 3 - the stringed arguments
  2985.  * 3.  save copy of arguments at offset 2.
  2986.  * 4.  On original argv, process white space and convert to OS2 Argument string.
  2987.  * 5.  Set up program name at offset 2 as ~argv
  2988.  * 6.  Convert zero length args to "~" and args beginning with ~ to ~~
  2989.  * 7.  Build OS2 Argument string (at last).
  2990.  */
  2991.  
  2992.     argc = CountNumberArguments (argv);
  2993.  
  2994.     if ((SavedArgs = (char **)AllocateMemoryCell ((argc + 3) * sizeof (char *)))
  2995.         == (char *)NULL)
  2996.     return -1;
  2997.     
  2998.     memcpy (SavedArgs + 2, argv, (argc + 1) * sizeof (char *));
  2999.  
  3000. /* Set program name at Offset 0 */
  3001.  
  3002.     SavedArgs[0] = *argv;
  3003.  
  3004. /* Build OS2 Argument string in Offset 1 */
  3005.  
  3006.     if ((SavedArgs[1] = BuildOS2String (ProcessSpaceInParameters (&argv[1]),
  3007.                     CHAR_SPACE)) == (char *)NULL)
  3008.     return -1;
  3009.  
  3010. /* Set up the new arg 2 - ~ + programname */
  3011.  
  3012.     if ((SavedArgs[2] = InsertCharacterAtStart (*argv)) == (char *)NULL)
  3013.     return -1;
  3014.     
  3015. /* Convert zero length args and args starting with a ~ */
  3016.  
  3017.     for (argc = 3; SavedArgs[argc] != (char *)NULL; argc++)
  3018.     {
  3019.     if (strlen (SavedArgs[argc]) == 0)
  3020.         SavedArgs[argc] = "~";
  3021.     
  3022.     else if ((*SavedArgs[argc] == CHAR_TILDE) &&
  3023.          ((SavedArgs[argc] = InsertCharacterAtStart (SavedArgs[argc]))
  3024.             == (char *)NULL))
  3025.         return -1;
  3026.     }
  3027.  
  3028. /* Build the full argument list */
  3029.  
  3030.     if ((OS2Arguments = BuildOS2String (SavedArgs, 0)) == (char *)NULL)
  3031.     return -1;
  3032.  
  3033. /* Build OS/2 environment string */
  3034.  
  3035.     if ((OS2Environment = BuildOS2String (envp, 0)) == (char *)NULL)
  3036.     return -1;
  3037.  
  3038. /* Exec it */
  3039.  
  3040.     ErrorCode = DosExecPgm (FailName, sizeof (FailName), fExecFlags,
  3041.                 OS2Arguments,
  3042.                 OS2Environment,
  3043.                 &rescResults, Program);
  3044.  
  3045. /*
  3046.  * What happened ?.  OS/2 Error - Map to UNIX errno.  Why can't people
  3047.  * write libraries right??  Or provide access at a high level.  We could
  3048.  * call _dosret if the interface did not require me to write more
  3049.  * assembler.
  3050.  */
  3051.  
  3052.     if (ErrorCode != 0)
  3053.     {
  3054.     switch (ErrorCode)
  3055.     {
  3056.         case ERROR_NO_PROC_SLOTS:
  3057.         errno = EAGAIN;
  3058.         return -1;
  3059.  
  3060.         case ERROR_NOT_ENOUGH_MEMORY:
  3061.         errno = ENOMEM;
  3062.         return -1;
  3063.  
  3064.         case ERROR_ACCESS_DENIED:
  3065.         case ERROR_DRIVE_LOCKED:
  3066.         case ERROR_LOCK_VIOLATION:
  3067.         case ERROR_SHARING_VIOLATION:
  3068.         errno = EACCES;
  3069.         return -1;
  3070.  
  3071.         case ERROR_FILE_NOT_FOUND:
  3072.         case ERROR_PATH_NOT_FOUND:
  3073.         case ERROR_PROC_NOT_FOUND:
  3074.         errno = ENOENT;
  3075.         return -1;
  3076.  
  3077.         case ERROR_BAD_ENVIRONMENT:
  3078.         case ERROR_INVALID_DATA:
  3079.         case ERROR_INVALID_FUNCTION:
  3080.         case ERROR_INVALID_ORDINAL:
  3081.         case ERROR_INVALID_SEGMENT_NUMBER:
  3082.         case ERROR_INVALID_STACKSEG:
  3083.         case ERROR_INVALID_STARTING_CODESEG:
  3084.         errno = EINVAL;
  3085.         return -1;
  3086.  
  3087.         case ERROR_TOO_MANY_OPEN_FILES:
  3088.         errno = EMFILE;
  3089.         return -1;
  3090.  
  3091.         case ERROR_INTERRUPT:
  3092.         case ERROR_NOT_DOS_DISK:
  3093.         case ERROR_SHARING_BUFFER_EXCEEDED:
  3094.         errno = EIO;
  3095.         return -1;
  3096.  
  3097.         default:
  3098.         errno = ENOEXEC;
  3099.         return -1;
  3100.     }
  3101.     }
  3102.  
  3103. /* If exec - exit */
  3104.  
  3105.     switch (Mode)
  3106.     {
  3107.     case OLD_P_OVERLAY:            /* exec - exit at once */
  3108.         while (1)
  3109.         (void)DosExit (EXIT_PROCESS, 0);
  3110.         
  3111.     case P_WAIT:                /* Get exit code    */
  3112.         return rescResults.codeResult;
  3113.  
  3114.     case P_NOWAIT:                /* Get PID or SID    */
  3115.     case P_NOWAITO:
  3116.     case P_DETACH:
  3117.         return rescResults.codeTerminate;
  3118.     }
  3119.  
  3120.     errno = EINVAL;
  3121.     return -1;
  3122. }
  3123.  
  3124. /*
  3125.  * Insert character at start of string
  3126.  *
  3127.  * Return NULL or new string
  3128.  */
  3129.  
  3130. static char    *InsertCharacterAtStart (char *string)
  3131. {
  3132.     char    *cp;
  3133.  
  3134.     if ((cp = (char *)AllocateMemoryCell (strlen (string) + 2)) == (char *)NULL)
  3135.     return (char *)NULL;
  3136.     
  3137.     *cp = CHAR_TILDE;
  3138.     strcpy (cp + 1, string);
  3139.  
  3140.     return cp;
  3141. }
  3142. #endif
  3143.  
  3144. /*
  3145.  * Execute a Function
  3146.  */
  3147.  
  3148. static bool near ExecuteFunction (char **wp, int *RetVal, bool CGVLCalled)
  3149. {
  3150.     Break_C            *s_RList = Return_List;
  3151.     Break_C            *s_BList = Break_List;
  3152.     Break_C            *s_SList = SShell_List;
  3153.     Break_C            BreakContinue;
  3154.     C_Op            *New;
  3155.     FunctionList        *s_CurrentFunction = CurrentFunction;
  3156.     SaveParameters        s_Parameters;
  3157.     FunctionList        *fop;
  3158.     GetoptsIndex        GetoptsSave;
  3159.  
  3160. /* Find the function */
  3161.  
  3162.     if ((fop = LookUpFunction (wp[0])) == (FunctionList *)NULL)
  3163.     return FALSE;
  3164.  
  3165. /* Save the current variable list */
  3166.  
  3167.     if (!CGVLCalled && (CreateGlobalVariableList (FLAGS_FUNCTION) == -1))
  3168.     {
  3169.     *RetVal =  -1;
  3170.     return TRUE;
  3171.     }
  3172.  
  3173. /* Set up $0..$n for the function */
  3174.  
  3175.     SaveNumericParameters (wp, &s_Parameters);
  3176.  
  3177. /* Save Getopts pointers */
  3178.  
  3179.     GetGetoptsValues (&GetoptsSave);
  3180.  
  3181. /* Process the function */
  3182.  
  3183.     if (setjmp (BreakContinue.CurrentReturnPoint) == 0)
  3184.     {
  3185.     CurrentFunction = fop;
  3186.     Break_List = (Break_C *)NULL;
  3187.     BreakContinue.NextExitLevel = Return_List;
  3188.     Return_List = &BreakContinue;
  3189.     New = CopyFunction (fop->tree->left);
  3190.     *RetVal = ExecuteParseTree (New, NOPIPE, NOPIPE, EXEC_FUNCTION);
  3191.     }
  3192.  
  3193. /* A return has been executed - Unlike, while and for, we just need to
  3194.  * restore the local execute stack level and the return will restore
  3195.  * the correct I/O.
  3196.  */
  3197.  
  3198.     else
  3199.     *RetVal = (int)GetVariableAsNumeric (StatusVariable);
  3200.  
  3201. /* Restore the old $0, and previous return address */
  3202.  
  3203.     SaveGetoptsValues (GetoptsSave.Index, GetoptsSave.SubIndex);
  3204.     Break_List  = s_BList;
  3205.     Return_List = s_RList;
  3206.     SShell_List = s_SList;
  3207.     CurrentFunction = s_CurrentFunction;
  3208.     RestoreTheParameters (&s_Parameters);
  3209.     RunTrapCommand (0);        /* Exit trap            */
  3210.     return TRUE;
  3211. }
  3212.  
  3213. /*
  3214.  * Print Load error message
  3215.  */
  3216.  
  3217. static void near PrintLoadError (char *path)
  3218. {
  3219. #ifdef OS2
  3220.     fprintf (stderr, "%s: ", path);
  3221.  
  3222.     if (*FailName)
  3223.     fprintf (stderr, "%s ", FailName);
  3224.  
  3225.     PrintWarningMessage ("%s\n", ConvertErrorNumber());
  3226. #else
  3227.     PrintWarningMessage (BasicErrorMessage, path, ConvertErrorNumber ());
  3228. #endif
  3229. }
  3230.